Skip to content

Instantly share code, notes, and snippets.

@dann
Created December 12, 2009 16:31
Show Gist options
  • Select an option

  • Save dann/254959 to your computer and use it in GitHub Desktop.

Select an option

Save dann/254959 to your computer and use it in GitHub Desktop.
package com.thinkminimo.step
import javax.servlet._
import javax.servlet.http._
import scala.util.DynamicVariable
import scala.util.matching.Regex
import scala.collection.mutable.HashSet
import scala.collection.jcl.MapWrapper
object Step
{
type Params = Map[String, String]
type Action = Any => String
// immutableな変数
val protocols = List("GET", "POST", "PUT", "DELETE")
}
import Step._
abstract class Step extends HttpServlet
{
// protcolsをmapしてる。
// Mapの _*はrubyで配列を展開して渡したりするときにやるのと同じ hoge( *array )
val Routes = Map(protocols map (_ -> new HashSet[Route]): _*)
// DynamicVariableはperlのlocal変数的なもの
//class DynamicVariable[A]
//{
// private val tl = new ThreadLocal
// def get : A = tl.get().asInstanceOf[A]
// def withValue[B](x : A)(f : => B) : B = {
// val old = get
// try {
// tl.set(x)
// f
// } finally {
// tl.set(old)
// }
// }
//}
val paramsMap = new DynamicVariable[Params](null)
var contentType = "text/html"
class Route(val path: String, val action: Action) {
val pattern = """:\w+"""
val names = new Regex(pattern) findAllIn path toList
val re = new Regex("^%s$" format path.replaceAll(pattern, "(.*?)"))
// Mapの _*はrubyで配列を展開して渡したりするときにやるのと同じ hoge( *array )
def apply(realPath: String): Option[Params] =
re findFirstMatchIn realPath map (x => Map(names zip x.subgroups : _*))
override def toString():String = path
}
override def service(request: HttpServletRequest, response: HttpServletResponse) {
val realParams = new MapWrapper[String, Array[String]]() {
def underlying = request.getParameterMap.asInstanceOf[java.util.Map[String,Array[String]]]
}.map { case (k,v) => (k, v(0)) }
//local関数
def isMatchingRoute(route: Route) = {
def exec(args: Params) = {
// _は何?
before _
// method callしてるだけ。.がなくていい
response setContentType contentType
// ++はコレクションの合体
//DynamicVariable クラスの withValue メソッドはある値とブロックをとり、
//渡された値で動的変数の内容を一時的に改変してブロックを実行します。
// ブロックの実行が終わると動的変数の値は元に戻る。
paramsMap.withValue(args ++ realParams) {
// actionはAny => Stringの関数なので、それを実行してる。
response.getWriter print route.action()
}
}
//getPathInfo returns everything after the context path, so step will work if non-root
route(request.getPathInfo) map exec isDefined
}
if (Routes(request.getMethod) find isMatchingRoute isEmpty)
response.getWriter println "Requesting %s but only have %s".format(request.getRequestURI, Routes)
}
def params(name: String): String = paramsMap value name
def before(fun: => Any) = fun
// dispatch tableつくってるのはここ。
// get("/x") blockをdispatch tableにつっこんでいるのはここ
// Routesにつっこんでって、mapした結果が最終的にListになってるってことを保証するためだけにListが使われている。
val List(get, post, put, delete) = protocols map routeSetter
// functional programming means never having to repeat yourself
// (=> Anyは何の略?)
// (String) => (=> Any) => Unit
// (String) => (()=> Any)) => Unit
// Stringをいれると、 (() =>Any) => Unitな関数を返す関数?
// x => fun.toStringの部分は、 xをいれると、fun.toStringを返す関数
private def routeSetter(protocol: String): (String) => (=> Any) => Unit = {
// fun.toStringは、fun().toString()している
// fun: => Anyは、 fun: () => Any の省略形。引数がない場合は省略できる。
// funは、() => Anyにする関数
def g(path: String, fun: => Any) = Routes(protocol) += new Route(path, x => fun.toString)
// (g _)部分
// functionName _ とすると
// 明示的に参照として扱える(?)
// var ref = function(hoge) { functionName(hoge); }
// の参照
// curryはまさにcurry化
// (String, String) => Stringみたいな型の関数を
// (String) => (String) => Stringみたいに
(g _).curry
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment