今天准备Lift codekata的时候又发现新东西了

那本著名的书上也没写,但是应该是比较常用的功能:如何绑定一个attribute。
有一段html,内容如下:

1
2
3
4
5
6
<div>
  <dl id="xxx">
    <dt>xxx</dt>
    <dd>yyy</dd>
  </dl>
</div>

要实现的是把“dl”的“id”动态绑定到一个值,这样可以直接使用数据库中的id,并且也方便jQuery操作。lift里面可以这样实现:
首先把上面的html改造一下:

1
2
3
4
5
6
<div>
  <dl entry:id="xxx">
    <dt>xxx</dt>
    <dd>yyy</dd>
  </dl>
</div>

然后Scala代码这样写:

1
2
3
      bind("entry", xhtml,
           AttrBindParam("id", Text(article.id.is.toString), "id")
      )

如何在Lift里用javascript的confirm

使用Lift的SHtml.a()设计ajax调用的时候,老版本的Lift不支持给onclick加入用户自己定义的javascript方法,新版本的有了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
   * Create an anchor tag around a body which will do an AJAX call and invoke the function
   *
   * @param jsFunc -- the user function that will be executed. This function will receive as last parameter
   *                  the function that will actually do the ajax call. Hence the user function can decide when
   * 				  to make the ajax request.
   * @param func - the function to invoke when the link is clicked
   * @param body - the NodeSeq to wrap in the anchor tag
   * @param attrs - the anchor node attributes
   */
  def a(jsFunc: Call, func: () =>; JsCmd, body: NodeSeq, attrs: (String, String)*): Elem = {
    attrs.foldLeft(fmapFunc(contextFuncBuilder(func))(name =>;
            <a onclick="{deferCall(Str(name" href="javascript://">{body}</a>))(_ % _)
  }

对于jsFunc可以这样理解:
用户定义了一个方法:

1
2
3
4
function delete(toDelete) {
  if (confirm("Delete?"))
    toDelete()
}

那么jsFunc可以这样定义:Call(“delete”),Lift会把它生成的ajax方法作为最后一个参数传递给delete方法。

慢慢地转到maven

开始把之前做的一些项目往maven上转了。先是jfetion,再加上今天做的smsd。

说来惭愧,jfetion做了很久了,但是自己一直都没弄个什么应用出来,一直在用的smsd还是最早的时候用C写的。今天老婆出去逛街,闲来无事,用Scala重写了一遍,已经可以正常工作了,其它的功能慢慢加吧。

基本想法很简单,后台启一个daemon,监视某个文件夹,如果里面有.sms文件,就把里面的内容发送给相应的人,当然必须是飞信上面的好友。

顺便说一下,Snow Leopard的Java默认是64位的,而libfetion没有Mac上64位的版本,所以要么在系统设置里面更改Java的默认设置,要么运行的时候给个参数:java -d32 …

Scala的XML是只读的

有什么好处呢?最直接的好处就是同一个node可以被多个xml共享,想想DOM是怎么处理这个的。

问题是一个简单的修改就需要重新建立一个完整的xml。Scala提供了BasictTransformer和RuleTransformer,再加上无敌的case匹配,可以方便地处理某类节点。

有个麻烦地地方,Scala-2.7.5的BasicTransformer有bug,只能用nightly或者等2.8发布了。

Scala确实非常适合实现DSL

用了一上午的时间,就实现了xpath的解释,类似这样的东西:a[@b=="1" and (c/@d==2 or e[f=="abc"])]。再加上Scala对函数对象的支持以及函数名的随意性,很容易就实现了对xml操作的扩展。除了Scala定义好的”\”,”\\”,加入了”\%”来支持复杂的xpath,跟dom4j差不多了。真爽!

Scala的XML处理非常强大

而且好用。模型非常简单,比该死的DOM好太多了。

Lexer的直接支持使得XML使用起来就像是写代码一样,这样可以直接把XSLT丢掉了。

目前还有些不是很好的地方,比如不能用变量作为tag的名字:<{tag_name}>,这样不行;再比如XPATH的支持还不全。不过好在可以implicit来增加新的方法,语法糖衣真是不错。

我是如何用Scala定义新异常类型的

Scala的构造方法非常灵活(具体怎么个灵活法这里不多说了),但随之而来的是重载构造方法的麻烦。

例如定义新异常类型。一般来说,自己定义的异常都属于checked异常,大都从Exception继承过来,所以也大都需要定义多个构造方法。如果用Java来定义,没什么好说的,重载就行,但是用Scala的话就有点麻烦。Scala规定所有重载的构造方法都必须调用或间接调用默认构造方法,所以必须使用如下的方法。

1
2
3
4
5
class MyException(message: String, cause: Throwable) extends Exception(message, cause) {
  def this(message: String): = this(message, null)
  def this(cause: Throwable): = this(null, cause)
  def this: = this(null, null)
}

当然,这样是可以工作的,但是仔细看看Throwable的实现就会发现如果传入的cause为null话会导致异常栈的丢失。而且最恶心的是Throwable没有提供相应的setter/getter,我们能做的就是调用构造方法。
所以我就想出了下面的怪招。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
object SpcException {
  def apply(message: String, cause: Throwable): Exception =
    (new SpcException1(message, cause)).asInstanceOf[Exception]
 
  def apply(message: String): Exception =
    (new SpcException2(message)).asInstanceOf[Exception]
 
  def apply(cause: Throwable): Exception =
    (new SpcException3(cause)).asInstanceOf[Exception]
 
  def apply(): Exception =
    (new SpcException4).asInstanceOf[Exception]
}
 
trait SpcException
 
class SpcException1(message: String, cause: Throwable)
    extends Exception(message, cause) with SpcException
 
class SpcException2(message: String)
    extends Exception(message) with SpcException
 
class SpcException3(cause: Throwable)
    extends Exception(cause) with SpcException
 
class SpcException4 extends Exception with SpcException

基本思想是定义一个trait,然后定义四种异常,每种都从该trait扩展并提供不同的默认构造方法,同时定义一个singleton,提供四种不同的apply方法用来构造四种不同的异常。这样就可以解决之前的问题,虽然不怎么好看。

Scala的Actor机制简要分析

Scala里多线程的基础就是Actor,核心思想是用消息传递来进行线程间的信息共享和同步。

Scala的Actor线程模型可以这样理解:所有Actor共享一个线程池,总的线程个数可以配置,也可以根据CPU个数决定;当一个Actor启动之后,Scala分配一个线程给它使用,如果使用receive模型,这个线程就一直为该Actor所有,如果使用react模型,Scala执行完react方法后抛出异常,则该线程就可以被其它Actor使用。