初探Tinyrb-1

Tinyrb,一个Ruby的实现,作者Marc-André Cournoyer计划实现参照Lua和Potion实现来做出一个轻量级快速的Ruby实现,这是一个对作者关于Tinyrb想法的采访。目前还不能直接运行mspec,或者是我不知道怎么运行mspec。

其中用了一些外挂:
GC:Boehm-Demers-Weiser conservative garbage collector
语法解析(Lexical Parse):peg/leg
命令行选项解析(Command-Line Option Parse):Free Getopt
正则表达式解析(Regular Parse):PCRE (Perl-compatible regular expression library)

Tinyrb到目前为止是个非常清晰简洁的实现,整个实现,VM+Ruby Library就200K多一点。编译运行非常简单,在Github上clone下来后make就行了。跑下Fib测试:


~/ws/tinyrb > time ruby bench/bm_app_fib.rb
real	0m10.866s
user	0m9.393s
sys	0m1.170s
~/ws/tinyrb > time ruby-1.9 bench/bm_app_fib.rb
real	0m1.827s
user	0m1.423s
sys	0m0.013s
~/ws/tinyrb > time jruby bench/bm_app_fib.rb
real	0m2.718s
user	0m2.447s
sys	0m0.097s
~/ws/tinyrb > time tinyrb bench/bm_app_fib.rb
real	0m1.884s
user	0m1.680s
sys	0m0.007s

运行环境:Archlinux,kernal26-2.26.29,Core T8100,2G Mem。或许Tinyrb目前实现还不完全,启动速度比其它的Ruby实现就快了不少。

现在来剖析一下源码,我们从Tinyrb解析器启动入手,整个解析器的启动是在vm/tr.c文件中定义的:


/* file: vm/tr.c */
int main (int argc, char *argv[]) {
  int opt;
  TrVM *vm = TrVM_new();
  while((opt = getopt(argc, argv, "e:vdh")) != -1) {
    switch(opt) {
      /* 参数解析 */
      ...
    }
  }
  /* 参数处理 */
  if (argc > 0) {
    TR_FAILSAFE(TrVM_load(vm, argv[argc-1]));
    return 0;
  }
  TrVM_destroy(vm);
  return usage();
}

可以看到整个VM的启动由TrVM_new()开始


/* file: vm/vm.c */
  /* GC初始化 */
  GC_INIT();
  /* [A]VM分配空间并初始化 */
  TrVM *vm = TR_ALLOC(TrVM);
  vm->symbols = kh_init(str);
  vm->globals = kh_init(OBJ);
  vm->consts = kh_init(OBJ);
  vm->debug = 0;
  /* [B]初始化几个核心类Method,Symbol,Class,Object,Module */
  TrMethod_init(vm);
  TrSymbol_init(vm);
  TrModule_init(vm);
  TrClass_init(vm);
  TrObject_preinit(vm);
  TrClass *symbolc = (TrClass*)TR_CORE_CLASS(Symbol);
  TrClass *modulec = (TrClass*)TR_CORE_CLASS(Module);
  TrClass *classc = (TrClass*)TR_CORE_CLASS(Class);
  TrClass *methodc = (TrClass*)TR_CORE_CLASS(Method);
  TrClass *objectc = (TrClass*)TR_CORE_CLASS(Object);
  /* [C]设置核心类的继承体系 */
  symbolc->super = modulec->super = methodc->super = (OBJ)objectc;
  classc->super = (OBJ)modulec;
  /* [D]设置核心类的MetaClass */
  symbolc->class = TrMetaClass_new(vm, objectc->class);
  modulec->class = TrMetaClass_new(vm, objectc->class);
  classc->class = TrMetaClass_new(vm, objectc->class);
  methodc->class = TrMetaClass_new(vm, objectc->class);
  objectc->class = TrMetaClass_new(vm, objectc->class);
  /* [E]确保所有在Object之前创建的的Symbol的类得到初始化 */
  TR_KH_EACH(vm->symbols, i, sym, {
    TR_COBJECT(sym)->class = (OBJ)symbolc;
  });
  /* [F]装入各种核心类 */
  ...
  /* [G] */
  vm->self = TrObject_alloc(vm, 0);
  vm->cf = -1;
  /* [H]缓存几个常用的值 */
  vm->sADD = tr_intern("+");
  vm->sSUB = tr_intern("-");
  vm->sLT = tr_intern("<");
  vm->sNEG = tr_intern("@-");
  vm->sNOT = tr_intern("!");
  /* 装载Ruby Library(在lib/目录下的文件) */
  TR_FAILSAFE(TrVM_load(vm, "lib/boot.rb"));

在GC完成初始化之后,代码中[A]部分完成了维护整个Tinyrb运行环境的虚拟机对象的空间分配和初始化,VM是个宏:

#define VM TrVM *vm

TrVM这个VM的结构包括了什么:

/* file: vm/tr.h */
typedef struct TrVM {
  khash_t(str) *symbols; /* 全局的符号表 */
  khash_t(OBJ) *globals; /* 全局对象 */
  khash_t(OBJ) *consts;  /* 全局常量 */
  OBJ classes[TR_T_MAX]; /* 虚拟机维护的核心类 */
  TrFrame *top_frame; /* 最顶层的栈幀(运行时栈的栈顶) */
  TrFrame *frame; /* 当前的栈幀 */
  int cf;   /* 栈幀的数量 count of frames */
  OBJ self; /* 根对象 */
  /* 调试和错误符号,还有一堆异常 */
  ...
  /* 几个缓存的对象 */
  OBJ sADD;
  OBJ sSUB;
  OBJ sLT;
  OBJ sNEG;
  OBJ sNOT;
};

在前一块代码中设置的symbols,globals,consts就是保存运行时(Runtime)环境的各种信息,这几个变量都是Hash。接着下面的是Tinyrb的几个核心类列表,这是一个枚举值。然后是运行环境必不可少的栈帧,作用域调用就是靠这个维护的。栈幀由对象栈幀,栈顶,栈幀数这几个变量维护。还有一个虚拟机环境的根对象,这个对象就是在整个运行环境最外层作用域的对象,Ruby能做到不像Java那样写个什么都要包覆在一个对象中就是靠这个对象实现的,这个对象混入了Kernel模块,后面会看到每个栈帧(TrFrame)中都会有一个这样对象存在。最后是调试标记和异常信息,暂时略过。最后的几个常用的对象,可以看到在TrVM_new()中的[H]处进行初始化。

在VM的初始化中,中间的大段代码就是复杂完成Tinyrb的对象体系的构建,由Method开始初始化:


/* file:vm/class.c */
void TrMethod_init(VM) {
  OBJ c = TR_INIT_CORE_CLASS(Method, Object);
  tr_def(c, "name", TrMethod_name, 0);
  tr_def(c, "arity", TrMethod_arity, 0);
  tr_def(c, "dump", TrMethod_dump, 0);
}

TR_INIT_CORE_CLASS这个宏会引发一连串复杂的调用:


/* file:vm/tr.h */
#define TR_INIT_CORE_OBJECT(T) ({ \
  Tr##T *o = TR_ALLOC(Tr##T); \
  o->type  = TR_T_##T; \
  o->class = vm->classes[TR_T_##T]; \
  o->ivars = kh_init(OBJ); \
  o; \
})
#define TR_CORE_CLASS(T)     vm->classes[TR_T_##T]
#define TR_INIT_CORE_CLASS(T,S) \
  TR_CORE_CLASS(T) = TrObject_const_set(vm, vm->self, tr_intern(#T), \
                                   TrClass_new(vm, tr_intern(#T), TR_CORE_CLASS(S)))
/* vm/class.c */
OBJ TrClass_new(VM, OBJ name, OBJ super) {
  TrClass *c = TR_INIT_CORE_OBJECT(Class);
  TR_INIT_MODULE(c);
  /* if VM is booting, those might not be set */
  if (super && TR_CCLASS(super)->class) c->class = TrMetaClass_new(vm, TR_CCLASS(super)->class);
  c->super = super;
  return (OBJ)c;
}
#define TR_INIT_MODULE(M) \
  (M)->name = name; \
  (M)->methods = kh_init(OBJ); \
  (M)->meta = 0

TR_INIT_CORE_CLASS(Method, Object);展开如下


TrClass *obj = TR_ALLOC(TrClass);
obj->type  = TR_T_Class;
obj->class = vm->classes[TR_T_Class];/* 实际上这句将其赋值为0,因为VM内部还没有创建Class类型 */
obj->ivars = kh_init(OBJ);
obj->name = tr_intern(Method);
obj->methods = kh_init(OBJ);
obj->meta = 0;
obj->super = vm->classes[TR_T_Object]; /* 实际上这句将其赋值为0,因为VM内部还没有创建Object类型 */
OBJ const_class = TrObject_const_set(vm, vm->self, tr_intern(Method), (OBJ)obj);
vm->classes[TR_T_Method]=const_class;

这样很清楚看到Method这个类初始化的过程,首先分配一个类(TrClass)的内存空间,接着设置所有的属性,之后把这个Method类设置到VM的常量列表,最后将这个Method类的对象地址保存到虚拟机的类型列表(Classes List)。注意这里其实Method类的class和super都是为0的。

顺便提一下,Tinyrb内部的对象类型(Object type)就是这个枚举值。


/* file: vm/tr.h */
typedef enum {
  /*  0 */ TR_T_Object, TR_T_Module, TR_T_Class, TR_T_Method, TR_T_Binding,
  /*  5 */ TR_T_Symbol, TR_T_String, TR_T_Fixnum, TR_T_Range, TR_T_Regexp,
  /* 10 */ TR_T_NilClass, TR_T_TrueClass, TR_T_FalseClass,
  /* 12 */ TR_T_Array, TR_T_Hash,
  /* 14 */ TR_T_Node,
  TR_T_MAX /* keep last */
} TR_T;

当Method的属性设置完成之后接着就开始为其添加方法name(),arity(), dump()。添加方法是通过之前已经设置好的Method类,实例化三个Method Object,并把这三个对象添加到Method类的方法列表中,下面是tr_def(c, “name”, TrMethod_name, 0) 的展开:


TrMethod *method = TR_INIT_CORE_OBJECT(Method); /* 初始化一个Method对象并返回 */
method->func = TrMethod_name;
method->data = TR_NIL;
method->arity = 0;
TrClass *m =  (TrClass*)E(vm->classes[TR_T_Method])
TR_KH_SET(m->methods, name, method);
method->name = name;

接着其它几个类型也类似的过程进行初始化:Symbol,Module,Class和Object。

到此为止VM的Const List已经有了这几个类Method,Symbol,Module,Class,Object,并且它们的第一个实例也已经保存到Classes List中。接着这些类的继承体系和剩下的核心类怎样初始化呢?请听下回分解。

runtime error words file not found

最近运行测试时在跑到用dm-sweatshop创建fixtures时总是报RuntimeError words file not found,而出错的行包含/\w+/.gen,然后就开始折腾…

最初看到/\w+/.gen这种语法时觉得十分的新奇,还以为是dm-sweatshop的创意,原来是dm-sweatshop依赖的randexp库的魔法,而这背后原来是从系统的words文件中随机挑选出一个单词来实现的。words file in Wikipedia

如果系统中没有words文件那在使用刀sweatshop的/\w+/.gen时就一直会报RuntimeError words file not found。而就是这个没有明确说明的依赖,让我折腾了好多天,因为系统中不一定一开始就有这个words文件,像在我用的ArchLinux上是由 words包提供的(貌似Ubuntu中是wbritish包)。

一开始我以为是DM又抽风了,就删了dm,然后又把整个rubygems删了。然后开始用grep和find搜索各种源码,先是在dm-more中找不到,接着去找ParseTree(sweatshop依赖它来完成unique {/\w+/.gen})语法。最后读了一下sweatshop依赖中看到randexp,接着在randexp中搜索到了”words file not found”这个RuntimeError。

以上的做法比较S13,如果在使用sweatshop之前RTFM,就能看到它依赖于Randexp,然后如果再去看看源码,就不用折腾那么久XD

不过仔细读了一下sweatshop的源码,知道其中的猫腻,sweatshop为DataMapper::Model模块加入了几个方法:

fixtures:定义fixtures,和FactoryGirl的define差不多,可以用个Symbol指定名字,否则就为:defualt。方法缩写fix。
generate:使用之前fixtures方法中定义的属性值创建模型实例,调用的是模型的create方法。缩写gen。
generate!:同上,不过故名思义创建实例调用的方法是create!。缩写gen!。
generate_attributes:返回在fixtures方法中定义的属性Hash。类似FactoryGirl的attribute_for。
make:调用new创建实例,即不保存。
pick:返回一个由上述创建实例的方法创建的实例。一般用于关联,要将已创建的模型实例作为关联对象时使用。
sweatshop中维护了两个Hash,维护模型定义的model_map和维护实例(包括保存与未保存)的record_map。上述方法中 fixtures将模型定义加入model_map,gen/gen!/make方法则在创建实例后将其加入record_map中,pick方法就在 record_map中找出现存实例。

如果想要在FactoryGirl中使用/\w+/.gen就require ‘rendexp’这句就行了。

最后有个疑问,Rendexp这个包在win平台能用吗?

learning jquery notes

以下内容是Learning JQuery一书的前六章笔记摘要,基本的JQuery操作就在这里了。很多地方只是提一下,具体要用时还是去查官方文档

Selecter

基本通过工厂方法$()进行选择,可以选择标签名(Tag),ID(id),类(Class)。

通过子元素组合符(>)可以选择到后代元素

当需要通过属性进行选择时可以使用XPath语法:tag[@attribute]。更好的选择过滤符$=,^=,*=。

JQuery特有的自定义选择符,以冒号开头(:)

  • :not 反向选择
  • :eq(index) 通过下标对数组进行选择
  • :odd 索引为奇数的元素
  • :even 索引为偶数的元素
  • :nth-child(index) 通过索引访问子元素
  • :contains(string) 包含指定字符串的元素

选择到元素后可通过JQuery的特有遍历方式进行遍历

filter() 可以调用自定义选择符进行过滤
parent() 取得父元素
next() 取得下一个同辈元素
siblings() 取得所有同辈元素
find() 通过标签加选择符进行过滤

通过get(0)或者$(el)[0]可以直接返回DOM元素

Event

$(document).ready()是注册事件处理器的入口,它会在HTML下载完毕并解析为DOM树时自动执行,并且是按顺序执行注册的处理器。

一般的形式:


$(document).ready(function(){
    # codes....
});

事件绑定是使用bind()函数进行的,常见形式:


$(el).bind(event, function-define);

bind到元素上的事件不会覆盖,即可为元素同个事件定义多个事件处理器并让其按顺序执行。

bind()的简写形式是直接调用与事件同名函数,如click(functions-define)。

toggle()方法接受两个函数参数,在第一次单击时会调用第一个函数,第二次则调用第二个函数并循环这个过程。

toggleClass(classname)方法会为元素切换指定的类。

hover()方法与toggle()方法类似,接受两个函数的参数,分别在鼠标指针进入和离开该元素范围时执行。

unbind()方法可以解除元素的事件处理器绑定,提供两个参数,一个事件名,和函数名。

one()方法可以绑定一次性事件,即绑定完成后解除。

trigger()方法可以模拟事件的发生。参数为事件名。其简写形式为不带任何参数的事件名同名函数。

Effect

css()方法可以接受属性名和值来对元素的style进行设置。两种调用形式css(property, value),css(property1 : value1, property2 : value2…)。

css()方法也可以取得对应属性名的值。

BTW,css()方法类似reader和writer。

hide()和show()方法可以对元素进行隐藏和显示。

所有的效果都可以加上速度参数:slow normal fast。

fadeIn(),fadeOut()和fadeTo()修改元素的不透明度。

slideDown()和slideUp()修改元素的高度。

animate()方法可以同时修改元素的多个属性。

效果函数可以带上第二个参数作为回调函数,在效果完成后调用。

效果的顺序原则:

  1. 一组元素上的效果
    • 当在一个.antimate()方法中以多个属性的方式应用时,是同时发生的。
    • 当以方法连缀的形式应用时,是按顺序发生的(排队效果)。
  2. 多组元素上的效果
    • 默认情况下是同时发生的。
    • 当在事件处理程序的回调函数中应用时,是按顺序发生的(排队效果)。

DOM

addClass()和removeClass(),togleClass()可以对元素的类进行增删。

attr()和removeAttr()可以对元素属性进行操作。

each(function(index){})可以对元素组进行迭代。

clone()方法可以进行深度复制

insertBefore(),before(),insertAfter(),after()方法用于在相邻位置插入新元素。insertXxx和Xxx存在着被动与主动的关系。

append(),appendTo(),prepend(),prependTo()用于元素中插入新元素。

wrap()用于在元素外插入新元素,即用新元素包裹自身。

html(),text()使用新元素或文本进行替换

empty()移除元素

remove()移除元素及其后代元素

Ajax

load()方法可以直接请求html片段并直接插入元素中。

$.getJSON()方法可以请求json文档,第二个参数可以带上回调函数。

$.get()和$.post()两个方法可以模拟GET和POST请求,并支持回调函数。第二个参数可以加上一个参数map。

$(’#form’).find(’input’).serialize()可以序列化表单数据。

ajaxStart()和ajaxStop()是两个Ajax请求的监听器,在请求开始时会调用注册在前一个函数的回调处理器,在请求结束时会调用后一个函数的回调处理器。

config files

https://sites.google.com/site/kaionunix

Moved to Github:
http://github.com/kaichen/archlinux-config/tree

Include confirguire files of Linux, awesome wm, vim, etc.

晒历史

[ ~ ]$ history | awk {'print $2'} | sort | uniq -c | sort -k1 -rn | head
    140 v
    120 git
     61 ls
     55 y
     49 rake
     46 exit
     30 sudo
     30 p
     29 killall
     27 cat

PS. v=vim, y=yaourt, p=pacman.

这么多的包管理命令,看来我太无聊了…

database.yml & capistrano2

实验室的服务器配置好了apache2+passenger2的环境,就很开心试了capistrano2远程部署。

要使用capistrano2来作远程的deploy当然要把服务器配置好ssh,web server等,我的做法是创建一个deploy的user专门来干这档事,并把deploy加入到www-data组以便在服务器的空间有权限写入。不过在试用过程中发现如果是使用scm的话capistrano2是不管你database.yml(因为不可能把production server的db账户信息纳入版本控制),想想可能放在share目录下应该就可以解决(share目录专门放些不变的东西,如log,如一些如用户资源等静态数据),但具体怎样还是看看别人怎么做的,搜索一下看到这个POST。这位老兄加了个两个钩子,在setup和update_code时把事先准备的模板放到share目录中。但其实还有些问题,数据库的user并不一定就是server的user,最好还是在config/deploy.rb加上两个字段如db_user和db_passwd来设数据库。

看了下Capistrano2的文档,发现真是个好东东,简单又方便。

给自己放假

趁着国庆给自己放假两天。

那天踢了一下球之后那天晚上基本无法工作,太累,就简单地作一些翻译的文档的整理。第二天(亦就是昨天2号)也是很累,全身酸痛,从肩膀到腰到腿,好像才两个月没有踢球就这样了,看来身体越来越不行了。然后懵懵地就到了晚上,基本不知道自己在作什么,晚上有点精神,配置了一下实验室的服务器,把redmine从lighttpd迁移到apache2+passenger2的环境,并把svn库整理了一下。服务器上的所有软件包作更新,删除掉无用的包,再换个sever的内核(Ubuntu系统,原来用generic内核),还有更新rubygems。顺便把服务器的配置写份文档,并写了几个配置脚本,以后交给师弟来弄了。由于把服务器配置为a2+p2所以试了一下部署,结果发现capistrano一些问题,下篇post再谈。

今天早上起来又很困,结果就顺便把自己的电脑的环境清理一下。上个星期因为自己编译了gnome2.24结果把整个gnome搞坏了,所以最近一直用着KDEmod,其实很不爽。这次就趁火打劫地把gnome和kdemod整个完整删掉,换个轻量级的环境,一开始是openbox+rox+fbpanel+feh,配置挺烦一下,弄到最后换成openbox+rox+lxpanel。中间折腾了一下panel,又折腾了一下session,还要分配好rox和openbox的分工(有多种分工方法,最后选了rox负责桌面背景但不接管桌面,openbox接管桌面菜单)。因为有段时间看过openbox和rox的文档,所以用起来觉得还挺习惯,快捷键的操作很爽。下午再把firefox清理了一下,直接删了配置目录,自己再去下插件,网速有些问题,弄了一个钟。晚上学习如何查英文paper,大概摸出个门道。

在配置服务器的时候发现Ubuntu和其它发行版的目录结构差好多,比如apache的配置方法和在arch和贱兔的差好多,模块和站点的设置竟然是要在XX-available目录的文件link到XX-enable目录中,不是直接写个文件丢进去。本来还想配个git仓库的,后来觉得算了。在清理系统时,发现旧的rubygems竟然占了1G多,原来很多过时的gems还呆在里面。遂运行gem cleanup,过程很漫长,觉得那个依赖计算也是有问题,而且有内存泄漏问题,竟然有段时间把我的内存吃完了(2G),不知道是程序的问题还是GC的问题。

什么叫放假,不干活就是放假。

open source camp guangzhou小记

第一次参加开源活动,感觉不错,不过topic都是科普性质,有些失望。会议上其实cpug的大妈带了一帮人马来会课,还霸占了M1会议室+_+,而大部分topic都讲得比较科普。我去听的三个topic是老外讲agile,香港同胞讲香港活动,最后听了Pyer洗脑。petty chen和Pyer的前两个topic错过了。其中自己和老外有一些问答,感觉自己的英语至少有人能听得懂了,咔咔咔。和香港同胞有些讨论,会后想mail’他,可是发去的mail给弹回来了,他的msn,gtalk等都没有连上-_-

感觉这种活动是结交朋友为主的。看到很多传说中的大牛(其实很多不认识),如cpug的大妈(真人比相片年轻),petty chen,俞黎敏等等。和俞黎敏打了下招呼(他在blog post中提了下我^_^)。petty chen很有主持天赋。Pyer很拽。

PS:抽奖系统有点龊,不是随机序,是字母序循环,然后还不会自动去除已中奖的人,导致有人连拿两次奖。

会议相片在:
http://www.haokanbu.com/story/113420/
http://picasaweb.google.com/epaulin/OscampGuangzhou2008

Pages: 1 2 3 4 5 6 7 8 ...12 13 14 Next