Ruby的测试一些记录

最近在写一些ruby程序,自然了,要做一些测试什么的,RSpec无疑是一个非常好的帮手。但是很多人都以为RSpec是用来测试Rails程序的,其实RSpec不止局限于Rails程序,它是一个非常通用的测试框架,不过网上大部分例子都是围绕Rails的,所以给大家带来了一些误解。最近呢,RSpec发布了新版本,2.n,目前已经到了2.3。发布了大版本自然有很多改进,少不了也有许多令人不愉快的变化,尤其是对第三方的不思进取的其他工具的兼容性上就有些问题了。

首先是RSpec和aotutest结合,这个是使用RSpec的第一步,当然并不是所有人都会这么告诉你,但是基本上保留秘密的那些人就是这么用的。RSpec 2需要配置一个./autotest/discover.rb:

Autotest.add_discovery { "rspec2" }

你可以自己写一个,也可以让RSpec自己生成

rspec --configure autotest

效果是一样一样的。这样便可以使用RSpec了,无痛而且高效。

第二,我们要搞一个rr集成到RSpec中去,至于rr是什么,以及为什么要rr,这里简单说一下。rr是一个通用的Test Double框架,这里可能有点不太好懂,因为就算是自认为很了解Unit Test的人也未必真的知道这个词。不过好多童鞋已经知道了RSpec的mock,或者stub了,简单的来说:rr能够提供更好地mock和stub。更好意味着可以更加方便地解耦我们要测试的目标模块,这样意味着能够更细粒度的定位问题,从而获得更加有效的测试效果。

rr集成到RSpec,貌似也不是很难,不过这里要注意,RSpec的整体命名空间有所变化,现在都是以RSpec开头了,原来的那种以Spec开头的会得到警告信息。

RSpec.configure do |config|
  config.mock_with :rr
end

但是这里会稍微有个问题,autotest的时候,在一些情况下autotest不会加载这个spec_helper.rb文件,或者是加载的顺序有些问题,就会导致一些rr方法不能使用。处理办法就是在spec文件前面加入

require "spec_helper"

目前我还没有找到更好的方法,不过估计以后的ZenTest会处理好这个问题吧。或者是启动ruby解释器的时候加入-r这样的参数。

第三个,就是将Rcov结合进去了,做法也比较简单,但是要注意使用RSpec 2的命名。

require "rspec/core/rake_task"
 
RSpec::Core::RakeTask.new :cov do |task|
  sh 'rm -rf ./coverage/*'
  task.rspec_path = "rspec"
  task.rcov = true
  task.rcov_opts = %w{--exclude spec,rubygems}
  task.ruby_opts = %w{-rrubygems}
end

不过这个并不像想像的那么顺利,rcov调用这些例子的时候没有加载RSpec库,真是不幸,所以我们可爱的spec文件一般都会额外多加两行

require "rspec"
require "spec_helper"

这样基本能暂时解决目前的所有不适,可以轻松享用RSpec的好处。

另外我要说的就是,这些修修补补的工作本来不应该存在,这几个工具在今后应该考虑整合一下,友好地集成,毕竟像我这样愿意花时间配置的人不太多。尤其是Rcov,对RSpec2的支持显得有点跟不上了。

Flyspell的错误修复

至于具体怎么用flyspell搭配emacs参看下面这篇文章。

Linux 技巧:活用 Emacs 的单词拼写检查功能

我遇到的问题是flyspell不能正常启动,老是提示该死的

Enabling Flyspell mode gave an error

别提有多欠揍了。网上的说法基本都不对。为了验证我这aspell能工作,要这么着:

mars@mars-laptop:~$ echo "hello, woold" | aspell -a -l en
@(#) International Ispell Version 3.1.20 (but really Aspell 0.60.6)
*
& woold 14 7: wold, world, would, wild, Woolf, wools, Wood, wield, wood, wool, Wald, weld, wooed, wool's

貌似也没什么问题,关键就在这一步,要是我们不加“-l en”会怎么样呢?

mars@mars-laptop:~$ echo "hello, woold" | aspell -a 
Error: No word lists can be found for the language "zh_CN".

基本断定问题就出在这了,添加“lang en”到/etc/aspell.conf中,正常工作哈哈。问题就出在aspell默认是读取LANG作为默认语言的,对于使用中文作为默认语言的系统大家还是要多多注意啊。

MongoDB的备份和恢复

话说怎么备份本身就是一个非常值得深入探讨的问题。能把数据放置到别的地方,而后再拿回来使用,看似很简单,但实际应用中却是非常令人恼火的一件事儿。因为实际运行中,所有的数据都是流动的,以刻舟求剑的方式来作数据的备份和恢复就很不靠谱了。但同时,任何应用于生产环境的数据存储方案首先要考虑的就是备份融灾问题。可能你的机房里不会有大象,但是说不定会有老鼠呢。可能你的操作人员不小心拉错了闸。这些事情貌似都不太可能发生。但是以我的实际经验来看,如果你没有备份的话,倒霉的事儿发生的几率就会成几何级数增长。也就是说备份了就备份了,要是不备份,倒霉的事儿总会发生在你头上。所以还是小心一些。另外说一句,备份是非常不符合精益思想的,起码原生态的简单备份有着明显的浪费,而且若是做不好监控的话,出故障的几率并不低,所以千万不要觉得备份就万事大吉,仅仅是运维的第一步而已。

有两种比较简单的备份方法(直接copy数据库文件,mongodump),不太能单独在生产环境中使用。比较建议的做法是构建Master-Slave,在Slave上做些备份,slavedelay是个非常有用参数,要是不想搞得很麻烦,就可以设置几个不同的slavedelay值的slave,这样就可以应对很多情况了。有人问了,如果有些延时,那么岂不是会丢失一些数据。说对了。首先备份并不能保证完全不丢失数据,任何地球上已知的方法都不能保障这一点。其次,备份数据也要考虑备份的目的。其实并不是所有情况都是要回复到最新的数据的,想一想有人用大量垃圾数据填充你的数据库你怎么办。应对这样的情况就不能简单地恢复到最新的备份了是吧。

在slave上,可以定期地加锁,备份,然后解锁。总之了,要在slave展开热备份的一切操作,甚至为备份建立专门的slave。比如视觉中国的图片存储方案中的备份就是这么做的。

今天学到的几个shell工具

大部分来自 25个ssh技巧,不过我就纳闷了copy-id算是技巧么。
pv 用来查看pipe的速度。
yes 不停地返回某个字符串,其实我觉得这个应该是写Unix程序模仿的第一个对象。
当然了yes和pv搭配起来就能看看某种管道的速度是多少。尤其是远程的情况,比如搭配了ssh。

cstream 可以限制流量的好东西

echo w00t, i’m 733+ | cstream -b1 -t2

另外,tcpdump有个替代品啊tshark,也是很不错的。不知道现在的libpcap,有没有用到mmap这样的技术,有个牛人弄了libcap-mmap在08年的时候就能在10G网络工作,真是牛的一塌糊涂啊。哪天听听ifconfig讲一下这个。

ack 是一种更好的 grep, 完整perl风格的正则支持。

索引,做还是不做,这是个问题

先说个题外话很多人都搞错了mongodb的名字,错误地拼成了mangodb,或者mango。我劝大家不要吃芒果了。从我的网站搜索结果来看拼成mangodb的真的不在少数,这里还要请大家特别注意,尤其是母语不是英语的人。否则要是到英语讨论组提问的话,会引起不必要的误解。
首先要说明的是mongodb的索引和传统的关系型数据库的索引几乎完全一样,所以很多原来的优化手段这里也是适用的,反之也对。那么索引是什么呢?有些人觉得我故弄玄虚,玩数据库的谁人不知索引啊。好吧,那我换一个问法,索引用来干什么?索引是为了在查询的时候更加快速。bingo!这句话是有好多隐含的意思的。查询更加快速,那么是和什么相比呢?肯定是和笨方法相比了。所谓的笨方法就是复杂度为O(n)的线性查找了。这种查找的好处就是不用做任何预处理,而且不需要额外的空间,可以容易地并发。据我所知有两种方法可以降低时间复杂度,一种利用排序的方法,可以将查找有序表的时间复杂度降到O(log(n)),一种使用Hash的方法,用空间换时间,使得时间复杂度讲到O(1)。今天要讨论的主要集中在变无序为有序的做法,也就是大部分索引的机理。
既然涉及到排序,那么就涉及到顺序。所以按照{a:1, b:1}建立的索引,和{b:1, a:1}建立的索引是完全不同的。这种不同的表象是建立索引的速度会用不同,深层次的原因就是排序的准则不同所以顺序就不同,导致的排序复杂程度就不一样。要知道排一几乎有序的数列,和排一个几乎无序的数列可能是完全不同的工作量。还记得所有的算法书上都将到了最好情况和最坏情况吧。所以以一定要考虑你自己的数据究竟是什么样子的,本身有什么性质,建立索引的时候要尽量的符合现有顺序。少折腾,会有很大的获益。至于查询的时候,查询优化器不会计较{a:20, b:30}还是{b:30, a:20}会自动优化的。还要考虑的一个问题就是索引的复用,因为如果建立所以是{a:1,b:1,c:1,d:1},下面这几种都将能复用这个索引{a:10}, {a:10,b:20}, {a:10,b:20,c:90},查询关键字的顺序并不重要,但必须是建立索引的前几个关键字的组合,前一个,前两个,以此类推。
上面讲了第一顺序,还有另外一个顺序。那就是关键字的排序顺序。当然要是你的索引只有一个关键字,就没所谓了。要是有多个关键字,首先要考虑关键字摆放的顺序(上面讲到的),然后就是关键字本身怎么排的问题。是{a:1, b:-1}还是{a:1, b:1}。还是那句话,少折腾就多获益。
不要对不怎么变化的值进行索引。比如性别,有无驾照这种布尔类型的量,对这个索引是没有任何意义的。排不出顺序,也就失去了索引的意义。
有些时候需要返回大量结果,比如返回的结果占到集的一般,这时候,最好就别用索引, 老老实实的用笨方法更快。因为当你要把一本书的前半本都读出来时,索引帮不上什么忙的,反而显得很这腾。所以不见得索引就一定快,要适合自己才是关键。

巧用oplog

by 10gen’s Mathias Stearn
翻译:程显峰

你已经使用了热备(还没有?),也已经知道设置起来非常容易。但可能你还不知道:所有的操作都会存储在一个常规的固定集中,在主控上是local.oplog.$main,在备份上是local.oplog.rs。通过查询这个集,可以深入探究系统的各个方面,甚至可以实现异步触发器。

?View Code JAVASCRIPT
> use local
switched to db local
> db.oplog.$main.find().sort({$natural:-1})
{ "ts" : { "t" : 1288884355000, "i" : 1 }, "op" : "u", "ns" :
"test.foo", "o2" : { "_id" : 1 }, "o" : { "$set" : { "i" : 2 } } }
{ "ts" : { "t" : 1288884349000, "i" : 1 }, "op" : "u", "ns" :
"test.foo", "o2" : { "_id" : 1 }, "o" : { "$set" : { "i" : 1 } } }
{ "ts" : { "t" : 1288884227000, "i" : 1 }, "op" : "i", "ns" :
"test.foo", "o" : { "_id" : 1, "some" : "thing" } }
{ "ts" : { "t" : 1288884225000, "i" : 1 }, "op" : "n", "ns" : "", "o" : { } }

可以看到有两次更新,一次插入,一次空操作(就是最下面的那个)。op可以是下列值:
i – insert
d – delete
u – update
c – command
n – no-op

空操作定期执行确保时效性。如果想过滤掉这些信息,在查询条件加入{op:{$ne:’n’}}就可以了。

很重要的一点就是所有oplog条目都是等幂的。例如上面的两次更新都是由{$inc:{i:1}}调用的,但是最后会转换成$set操作,这样就可以在以后多次重放。

无线折腾折腾记续集

前些天,有人非常无耻地使用我家无线。本来我的无线就是没有密码的,倡导大家共享的精神,怎奈有些人不讲道德,疯狂下载。一气之下挂上了WPA2。噩梦就从此开始了。

老婆的机器一切正常。我的机器不能正常上网了。时断时续,很是怪异。从我多年的抢险经验来说,这种情况是最倒霉的,因为几乎无法判断问题的具体位置。于是就得挨个排除,反正我也乐此不疲,顺便借口和老婆说修东西来升级一下家里的硬件,于是买了150M的新无线路由和USB网卡。

东西拿回来后,开始大张旗鼓的折腾。先是在别处试了一下新的USB网卡,很稳定,但是插在我的linux机器上不能启动。很是郁闷,驱动都找到了,但是接口就是不能up,折腾了半天也不行。换了路由器,依然不行,也不是路由的问题。但是切回不加密的方式就好了,说明可能是wpa2+aes与驱动不兼容,查了些资料,发现这个是不太可能的,因为从2.6.25以后的内核默认支持我的网卡,模块是ath5k。既然内核支持了,那么就说明兼容性不太成问题。

不过各种迹象都表明貌似我的机器有问题。现在怀疑是在原来内核不支持我的网卡时配置的驱动和现在的驱动有部分冲突。并且没有卸载干净,理由就是新买的USB网卡不能工作,在别的Ubuntu上很正常。这个网卡用的Realtek的芯片,包括BT4之类的都可以使用,应该没有兼容性问题的。

考虑到当初切到awesome下,乱搞一气装了wicd,想反正这东西也不稳定(现在来看不稳定也是有原因的),倒不如切换回network manager算了。但是于事无补,问题依旧。至于底层的东西,我最近也没有时间研究太多,还是暂时缓缓吧。另外说几句,原来无知啊,还搞了半天madwifi,其实屁用没有,内核已经内置了。不过貌似国内知道的人不多啊。

目前解决的方案就是全面重装,升10.10,然后老老实实干活,少折腾。可是备份,资料转移又是个问题。怎么才能最平滑地度过呢?

新玩具_1289266510

不错的Js Chart图工具:
http://www.highcharts.com/
还有一个什么来着??
http://raphaeljs.com/

这个配合node.js使用,兼容IE5.5。
http://socket.io

这个其实并非新玩具,Javascript实现的Scheme:
http://tryscheme.sourceforge.net/

另外发现YAML是一种很好的配置文件格式,可以比INI表现更丰富的层次关系和更明确的属性类型。
另外JSON是YAML的一个子集。

祝大家玩得开心,用着舒心~

量变到质变——移动互联网的革命

互联网客户端的开发虽然能在短时间内就造就少数个人富豪,但从长远来看还是需要强大财力支持的公司才能在最后的竞争中胜出。

说的简单点,公司这种有组织的模式在任何方面来看都要胜过个人的单打独斗。当然这是从整体上来说的,并不是说个人一定不会成功,而只是说公司更容易掌握资源,更容易获得比较优势。

互联网发展的另一端,则必定是公司的天下了,大量的移动客户端,带来的商机,除了客户端本身,必然还有服务端。很多人都用传统的思维去看待这个问题,觉得只不过是数据量变大了而已。但是我不这么看。昨天我的同学从日本给我打电话来说,他在日本的上网接入贷款是1G,下载一个Matlab,只需要几十秒。我除了听着羡慕嫉妒恨之外,立刻意识到,这个网速的量变已经足够导致质变了。传统上认为下载软件的核心工作是提高速度和稳定性,其中有个极其重要的功能就是断点续传。但是如果在这种告诉网络环境下重新审视这个问题,你会发现断点续传就显得那么微不足道了。而如何应对高速网络与低速磁盘IO才成为一个全新的核心问题。如果把两种速度看作天平两端的砝码,那么当改变足够多的时候,天平就会向另一端倾斜,使得问题的本质发生改变。客户端的数量也在发生着这样的改变,不仅仅是量的问题,由此导致服务端的质变,还需要我们静下心来仔细观察。

10:15 记于鼓楼大街

MongoDB中的数据类型

JavaScript中的数只有一种类型64bit的浮点数。但是mongo中有三种分别是32bit的整数,64bit的整数,64bit的浮点数。

在mongo的shell中修改数字的话,很可能会改变数字的类型,这个大家要特别注意。不过估计很少有人会在shell直接修改数据吧。

还有一个问题是64bit整数保存成64bit浮点数,可能会有误差,这个请童鞋们仔细思考,慎重使用。想看看究竟的同学可以自己构造一个大数存进mongo中(当然一定不是在JavaScript中执行的,要不我不就白说了),然后看看结果。

mongo本身还提供其他有趣的类型,注意这些类型并不是所有编程语言都支持的。比如有些语言没有正则表达式类型,有些没有符号类型,有些语言没有日期类型。mongo还支持大部分语言都没有的最大最小类型。另外,你真的可以把JavaScript代码存在mongo里面,而且有一个对应的code类型。不过这么用的时候要慎重,当年blade爷就破口大骂将代码存在数据库里的愚蠢行为,不知道将来有一天他会不会这么做呢?

JavaScript支持的类型实在有限,所以在这里要警告大家,一定不要轻易在shell中修改数据回存。