2008-06-12

《Professional ruby on rails》这本书里面,第一章就有个例子, 它有两个model,一个是recipe,一个是 ingredient,将 recipe和 ingredient做为父子关系,为了使得test case能通过,就需要改很多地方。 原料是菜谱的一部分,所以它在routes当中把搞成 recipe/1/ingredient/1 这样的URL。而自动生成的functional test里面,都是按照缺省的route来处理的。 所以我要在所有碰到 ingredient_path的地方改成 recipe_ingredient_path。 很多地方,包括test, controller,view 都要改,包括增加 :recipe_id=>1 这样的参数,以及把@recipe 这个参数添加到 path 或者 link_to 的参数当中。 改了很多地方,这个恐怕不能算是DRY吧。

2008 年 06 月 12 日 14:40 | SimonLei | Comment 共0条留言
2008-05-19

rails当中,如果要实现类似于组件的开发,需要使用 render partials 这种机制来实现。 现在,又多了一种方法: cells, http://cells.rubyforge.org/

cells 其实还是类似于 controller ,只不过它自带了view,而且可以继承,这一点比 controller 强。另外,如果当前cell的view找不到,还可以找父类的view,从而使得 在rails当中,拥有类似于组件的开发机制。不过现在还刚开始没多久,文挡不是很多。

2008 年 05 月 19 日 22:49 | SimonLei | Comment 共0条留言
2008-05-05

http://migrate4j.sourceforge.net/ 上一篇刚刚说到有模仿 acts_as_versioned 的东西出现,现在又来了一个模仿 db:migrate 的东西。虽然以前也有 liquibase http://www.liquibase.org/ 做 类似的事情,不过那个是用xml来定义的,我不是很喜欢。而这个migrate4j,则 纯粹是 java 的方式调用。有点类似于以前哪个老大说的,完全取代ant的一个build 工具,用纯java替代xml的方式,我个人比较喜欢这个。不仅不用去了解xml的schema, 而且还可以依赖编译检查来避免错误。前不久 ZhongHaoming 还提到,由于在xml当中不小心将 jelly 写成 jetty,导致调试了很长时间的事情。如果是使用java来调用的话,这种情况就可以 在很大程度上避免了。

2008 年 05 月 05 日 13:26 | SimonLei | Comment 共0条留言
2008-04-27

在rails里面用 acts_as_versioned 感觉很不错,这里还有 hibernate 版本的一个: http://www.jboss.org/envers/quickstart.html 说白了就是在 hibernate的事件当中来做这个事情,不错,有机会可以试一下。

Currently, the library has the following features:
  • versioning of basic properties (strings, integers, longs…)
  • versioning of embedded components, which are composed of basic properties
  • versioning of classes with simple, composite and embedded ids
  • versioning of one-to-one uni- and bi-directional relations (new!)
  • versioning of one-to-many bi-directional relations (new!)
2008 年 04 月 27 日 13:31 | SimonLei | Comment 共0条留言
2008-04-24

我的笔记本是双操作系统,ubuntu和windows xp。前一段时间一直在ubuntu上用,由于需要使用一些即时通讯工具, 例如QQ,所以在ubuntu里面有用virtualbox虚拟了另一个winxp。这样一来,搞得有一个真实的xp系统,一个虚拟的 xp系统。这还不算头疼。最近因为一些原因,回到真实的xp系统上,发现有些ubuntu上用习惯了的东西又没有了。 这个时候,要么再virtualbox一个ubuntu,要么装cygwin。这两种方案,都不爽。而且,由于平常用的一些文件在几个真真 假假的系统之间换来换去,很难保持一致性,比较头痛。

后来想到,干脆把物理硬盘当做虚拟机的硬盘,这样,不管是把xp当做host,还是把ubuntu当做Host,我都是只有两套系统。 只不过是哪个系统为 host,哪个系统为 guest 而已。我使用的virtualbox 版本是1.5.6,参考它的手册9.9

要注意的是,文档中也指出,这是个比较危险的动作,可能会造成硬盘数据的丢失。有个原则,就是这两个系统,都不直接访问 另一个系统的分区。我就是一块硬盘,四个分区,1为xp,2和3是ubuntu,4是swap。

首先,在ubuntu上,这个比较简单,参考文档,使用命令:

VBoxManage internalcommands createrawvmdk -filename ~/winxp.vmdk -rawdisk /dev/sda -partitions 1 -relative -register

这里有个权限的问题,因为/dev/sda 和 /dev/sda1 对普通用户是不可读的,所以需要修改一下权限。我是直接用 777,虽然不安全,不过省事。这里用 relative参数,就是说不用其他分区,因此只要这两个device的权限就行。

随后是mbr的问题。我给这个虚拟机让它直接使用自己的mbr,首先用 sudo apt-get install mbr,然后

install-mbr -f -p 1 winxp-pt.vmdk

这样就使得它有独立的mbr了。不过因为我不重装系统,所以这个步骤的意义其实不是很大。随后就很简单了, 启动virtualbox,新建一个xp的虚拟机,使用这块新做出来的硬盘。这里注意,有个选项必须要选上,在属性-> 常规->高级->启用IO APIC 刚开始我没选这个属性,每次启动虚拟机,虚拟机都黑屏,选上就好了。

其次,在winxp上,也使用命令行,差别不大,不过有一点,因为ubuntu使用分区2,3,光使用这两个分区做虚拟机, 启动的时候总是停留在 MBR 1FA这个地方,查了半天也没查出个所以然来。后来想算了,干脆把全盘当做虚拟机的 硬盘好了,只要在winxp下面不要去直接访问分区2,3,应该就没事。命令行如下:

vboxmanage internalcommands createrawvmdk -filename ubuntu.vmdk -rawdisk \\.\PhysicalDrive0 -register

然后就可以了。这样我就可以把双操作系统随时都一起启动起来,比较好玩。:D

2008 年 04 月 24 日 08:38 | SimonLei | Comment 共0条留言
2008-03-13

见: http://simile.mit.edu/timeline/

Timeline is a DHTML-based AJAXy widget for visualizing time-based events. It is like Google Maps for time-based information. Below is a live example that you can play with. Pan the timeline by dragging it horizontally.

按照它的文档,使用起来还是很简单的,自己只需要写一个 servlet 来产生 simile 所需要的那个 xml 文档就行。 当然,由于我的 weblog 很多(接近3000),一次返回,不仅会很慢,而且拉动起来对于浏览器的负载也不小,时不时 的就把我的ie给搞死了,firefox倒基本上没问题。

因此,重点就是如何“局部地”显示weblog。也就是说,只返回当前能看到的时间段当中的 weblog 集合。simile 的 wiki 上也有,不过语焉不详,我把用到的javascript 贴出来:
    <script language="javascript">
    var tl;
    var oldMinDate;
    var oldMaxDate;
function onLoad() {
  var eventSource = new Timeline.DefaultEventSource();
  var bandInfos = [
    Timeline.createBandInfo({
        eventSource:    eventSource,
        width:          "80%", 
        intervalUnit:   Timeline.DateTime.DAY, 
        intervalPixels: 100
    }),
    Timeline.createBandInfo({
        eventSource:    eventSource,
        width:          "20%", 
        intervalUnit:   Timeline.DateTime.MONTH, 
        intervalPixels: 500,
        showEventText: false
    })
  ];
  bandInfos[1].syncWith = 0;
  bandInfos[1].highlight = true;
  tl = Timeline.create(document.getElementById("my-timeline"), bandInfos);
  tl.getBand(0).addOnScrollListener(function(band) {
       var minDate = band.getMinDate();
       var maxDate = band.getMaxDate();
       if ( oldMinDate != minDate.toDateString() || oldMaxDate != maxDate.toDateString()) {
      oldMinDate = minDate.toDateString();
      oldMaxDate = maxDate.toDateString();
          eventSource.clear();
          Timeline.loadXML("/wipo/event.xml?minDate=" + minDate + "&maxDate="+ maxDate, 
                            function(xml, url) { eventSource.loadXML(xml, url); });
       }
    });
  Timeline.loadXML("/wipo/event.xml", function(xml, url) { eventSource.loadXML(xml, url); });
}
 Timeline.DurationEventPainter.prototype._showBubble = function(x, y, evt) {
  document.location.href=evt.getLink();
 }
var resizeTimerID = null;
function onResize() {
    if (resizeTimerID == null) {
        resizeTimerID = window.setTimeout(function() {
            resizeTimerID = null;
            tl.layout();
        }, 500);
    }
}
    </script>

这里有两个需要注意的,其一是 _showBubble 被我重载了,因为ie弹出那个bubble 实在是太慢了。 我干脆让它直接打开新链接。 其二是 不能用 date 直接进行比较,我发现 minDate == oldMinDate 总是等于false。后来发现用 string 来做比较就没事。

2008 年 03 月 13 日 14:52 | SimonLei | Comment 共0条留言
2008-03-11
做一个日期字符串的parser,按说是很简单的,但是偏偏出错:
SimpleDateFormat parser = new SimpleDateFormat( "EEE MMM dd HH:mm:ss zzzzzzz yyyy");
System.out.println( parser.parse( "Sat Feb 9 03:42:44 UTC 0800 2008"));
这个报错:
java.text.ParseException: Unparseable date: "Sat Feb 9 03:42:44 UTC 0800 2008" 
为了排除这个错,用排除法,把日期和月份换成数字之后就对了,然后才想起来,是 locale的问题。因为我的 系统是Locale.SIMPLIFIED_CHINESE,所以它只能解析 “星期六 二月 9 03:42:44 UTC 0800 2008” 这样的 字符串,而对于英文的 Sat Feb 就不认识了。解决的办法就是把locale设置为ENGLISH就可以了。
2008 年 03 月 11 日 15:36 | SimonLei | Comment 共1条留言

本来是去看 simile的那个 timeline 的: http://simile.mit.edu/timeline/ 结果看到了其他有意思的项目,其中比较有意思的就是 http tracker: http://simile.mit.edu/dist/httptracer/ 这个东西可以监视http的往来信息,说白了就是一个proxy,只不过替你把 request和 response 个拼起来了。对于web开发当中的一些调试,还是很有作用的。

2008 年 03 月 11 日 08:28 | SimonLei | Comment 共0条留言
2008-03-05
异常
PropertyAccessException: IllegalArgumentException occurred while calling setter 
of Category children;

@Entity
public class Category implements DataTreeObject<Category> {
    ...
    Set<Category> children = new HashSet<Category>();
    ...
    @Override
    @OneToMany(mappedBy="parent", cascade = {CascadeType.ALL})
    public Collection<Category> getChildren() {
        return children;
    }
    public void setChildren(Set<Category> children) {
        this.children = children;
    }
    ....
}

左看右看都没觉得hibernate的定义有问题。单步跟踪,发现hibernate试图往children里面 写一个Bag,这时候才发现,原来是 getChildren 这个方法,由于是从 DataTreeObject 接口 继承下来的,所以返回值不是 Set 而是一个 Collection。这么一点看起来不起眼的小错误,又 花了半个小时进行调试。郁闷。

2008 年 03 月 05 日 15:39 | SimonLei | Comment 共0条留言
2008-03-04

前面也提到过,wicket 有一个和 hibernate 进行绑定的东西叫做 binder: http://databinder.net/ 然后又发现一个支持将 bean 绑定到 wicket 的项目 http://wicketwebbeans.sourceforge.net/ 重要的是,发现 wicketwebbeans 支持 databinder。

也就是说,可以用 wicketwebbeans + databinder,实现一个 dynamic scaffold。只要给一个Class, 就可以实现自动的增加、删除、修改、查询。

参考了一下 wicketwebbeans 的例子,做了一个Scaffold。对于最简单的 Entity,还是很方便的, 效果也还凑合。不过对于稍微复杂一点,关联关系多一些的实体,就经常性的抛出一些异常,大概是对wicketwebbeans 的掌握还不够吧。

不过最可怕的还是一个 stackoverflow 的异常。而且,这个异常应该不是逻辑问题,而是 wicketwebbeans 对内存 管理的问题。如果只有10条记录,这个异常不会出现。如果到了20条,这个异常就开始往外抛了。由于不是很了解wicketwebbeans 在里面做了什么动作,现在这个问题还没有解决。

不过总的来说,用 wicketwebbeans 应该是可以做到一个不错的 scaffold的。只不过还不够完善,它的这个思路还是很不错的。 正在考虑是继续使用它还是另起炉灶自己做一个。毕竟 wicketwebbeans 看起来也不是很完善。

2008 年 03 月 04 日 15:00 | SimonLei | Comment 共0条留言
2008-02-29

http://www.coolcode.cn/archives/?article-238.html

找了大半天,终于找到一个比较不错的方案了。

所谓text overflow,其实就是文字超长时自动加 …的办法。 一开始没有想到 overflow 这个关键字,一直用 truncate 这个 关键字找,结果确实也找到了一些,包括 jquery 的几个 truncate 插件,都没有这个好使。可见搜索的时候,找对关键字很重要。

这个也不错: http://www.hedgerwow.com/360/dhtml/text_overflow/ 似乎使用起来更容易一些。

2008 年 02 月 29 日 22:28 | SimonLei | Comment 共0条留言
2008-02-25

loosejar: http://code.google.com/p/loosejar/

这个功能挺有意思,关键是使用起来也简单:

  1. Start your application or application server with -javaagent:loosejar.jar flag (loosejar.jar should obviously point to the correct path of the actual jar)
  2. Exercise your application to make sure that the classes get loaded into the JVM.
  3. Get loosejar analysis results via JMX console (open jconsole and run com.googlecode.loosejar.LooseJarMBean#summary() in MBeans folder) or on application shutdown (via regular console log).

loosejar can only be used on Java 1.5 or higher JVMs.

The project is developed and maintained by Kyrill Alyoshin.

2008 年 02 月 25 日 08:45 | SimonLei | Comment 共0条留言

最近经常能看到 dvcs 这个名词,也就是分布式版本控制系统。说来最早还是 Skyin 告诉过我一种分布式的vcs: darcs,当时感觉很神奇。没有中央控制服务器,也可以进行版本控制。 基本的工作原理就是在每个开发者本地保留一个本地的vcs,然后在进行合并。而现在有不少 的分布式版本控制系统基本已经成熟了,其中比较强的几个是: Git,linux kernel在使用;Mercurial,mozilla 在使用;Bazaar,Ubuntu在使用。

对我个人而言,其实使用svn也还算满意,使用dvcs 的意愿不是很强烈。不过这里有篇文章, 有对这几个dvcs的比较: http://www.dribin.org/dave/blog/archives/2007/12/28/dvcs/ 值得注意的是,其中 git 有一个对svn的支持叫 git-svn。注意它不是一个转换工具,而是直接支持 本地为svn,而整体是git的这种情况。mercurial 则只支持一个对 svn 只读的功能支持。

2008 年 02 月 25 日 08:36 | SimonLei | Comment 共0条留言
2008-02-23

Mylyn 是eclipse 3.3当中自带的一个task list的东西,它有个很好很强大的特点 就是支持context。也就是说,你可以针对其中的某个task来进行开发,而mylyn会记录 这个task的context,也就是与之相关的一些文件。这样你就可以在多个task之间进行 切换,而不会每次都要重新去找几个你上次为了某个task而编辑过的文件。

google code 所提供的 Project host 也不错,提供免费的svn空间,也包括wiki和 文件下载以及issue tracking 之类的。而通过 mylyn 对 google code 的支持,就 可以在 eclipse 当中显示 google code 上的issue了。还是很方便的。具体实现方式 见: http://www.jroller.com/alexRuiz/entry/using_mylyn_with_google_code

不过遗憾的是,目前其实只是一个query而已。也就是说,只能从google code 上同步下来, 而不能在eclipse修改它的状态以及添加comment。实际上也可以,只是通过一个内嵌的浏览器 去做这个工作而已。不过也不错了,知足了,呵呵。

2008 年 02 月 23 日 14:28 | SimonLei | Comment 共0条留言
2008-02-22
Wicket 的 hot reload 其实很简单,只要在web.xml当中不放 WicketFilter, 而放 ReloadingWicketFilter,然后在static当中,将需要关注的package注册进去,如下代码所示:
    static {
        ReloadingClassLoader.excludePattern("org.apache.wicket.*");
        ReloadingClassLoader.includePattern("org.apache.wicket.examples.*");
        ReloadingClassLoader.includePattern("org.simcom.*");
        Bundle[] bundles = Activator.getDefault().getBundle().getBundleContext().getBundles();
        for (Bundle bundle : bundles) {
            // 跳过 eclipse 本身的插件
            if ( bundle.getSymbolicName().startsWith( "org.eclipse")) continue;
            URL url = bundle.getEntry( "/bin/");
                try {
                    if ( url != null)
                        ReloadingClassLoader.addLocation( FileLocator.toFileURL( url));
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }
    }

因为我做的工作是要将插件当中的class也进行hot reload,因此还要遍历各个bundles,然后将这些 bundle的bin目录当做需要Hot reload的url entry。

2008 年 02 月 22 日 22:27 | SimonLei | Comment 共0条留言
 
Powered by Ruby_on_rails.Copyright © 2006, SimonLei