Posts Tagged ‘ ruby

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的支持显得有点跟不上了。

UNO牌Ruby源码实在是写得太烂了

最近研究UNO牌,想自己写一个,于是google一下,准备参考。还真的发现有人用Ruby写了一个,真不错啊。
http://www.merkisoft.ch/projects/uno/

由于估计是瑞士人写的,所以文档和程序的命名都是用的德语,看着好吃力,幸亏有google在线翻译,我把大部分文字都翻译成了英文。这是我第一次近距离接触德语,感觉怕怕的,语法甚是我辈所不能理解。

看着看着,我就惊讶了。这是什么烂程序啊。我一向以为欧洲程序员有严谨的作风,受过良好教育,守规矩,有各种好的习惯。看来并不是所有人都像DHH一样啦。我把一些心得体会记下来,警醒自己和同路中人今后不要走弯路。由于源代码只能看不能摸,哦,是不能发布,所以这里我不能贴出源码片段。

1. 有一个函数将近100行,它的名字叫做run。我在豆瓣上说我看见了一个100行的ruby函数,众人立刻对我表示深深的同情。有人说20行就已经很多了。看来今天我确实遇到极品了。那么这么重的坏味道究竟来自于哪里呢,哦原来作者用if实现了一个有限状态机(FSM),多么极品的想法啊。长的函数是代码坏味道之首,一定要重写。有限状态机的相关部分可以参考state_machine不要重复发明轮子DRY,更不要用Ruby重复发明轮子,我真的很在意。另外说一句state_machine的作者也算呕心沥血,花了多年时间才到这个程度的,大家应该能感觉到其功力深厚。

2. 没有大胆使用class。换句话说没有有效的封装。最典型的例子就是“牌”,UNO牌是一种桌面游戏,其基本元素就是一张张的牌。作者似乎定义了一些类,但是却没有一个是关于“牌”的。这就导致了在其他类的内部出现了大量的不相关的代码。没有关于“牌”的类,自然关于牌的函数也是凤毛麟角,这样对系统的主要逻辑产生了很大冲击。不易于看到程序清晰的主干。建议做一个Card类,将牌本身的一些动作封装进去。

3. 使用魔法数。靠数字辨别颜色,靠数字表明牌的类型。这个坏处就不用多讲了。别让大家需要猜谜语,于人于己都不是好主意。应该更多的使用常量和符号,杜绝使用魔法数。

4. 将一个功能分散到多个文件中实现。我承认的确有些复杂的功能需要把功能实现分散开。但是这里的情况完全不是这样的。本身一些定义很清晰应该在模型一层。其中有一个类的实例方法在三个不同的地方实现。问题是文件之间应该确保调用顺序,要在文件当中写明require。但事实上这项工作交给了调用者,也就是说调用者得到功能与其require加载的顺序有关,一旦加载顺序不对,程序就完蛋了。这样的东西纯属人祸,我相信有很多不明不白的程序就是这么完蛋的。建议在一个文件中实现相关功能,文件头部必须显式调用所依赖的库或文件。

5. 莫名其妙的命名,比如ok。

6. 自己发明的单元测试,美其名曰单元测试。建议使用标准的单元测试框架,或者使用Rspec这样的奇妙工具。测试的力度还应该加大一些,目前来看还远不到单元的级别。

其他一些想法

1. 设计上通讯应该和模型逻辑等分离。模型接口应该和通讯高度解耦,这样就可以用任意通讯程序替换,这也是提高可测性的考虑。

2. 命名要遵守一般原则。比如用UNO的标准用语来命名牌或者动作,而不要使用同义词。最好使用英语,其他语言的命名还是不提倡。

3. 使用reek这样的工具,保持警惕性,保持对坏味道敏感性。看看结果

mars@mars-laptop:~/uno-ruby/src$ reek . | grep "LongMethod"
  AdvancedSpieler#karteWaehlen has approx 15 statements (LongMethod)
  Mensch#karteWaehlen has approx 21 statements (LongMethod)
  Mensch#zeigeKarten has approx 9 statements (LongMethod)
  busy has approx 6 statements (LongMethod)
  Spiel#add has approx 7 statements (LongMethod)
  Spiel#run has approx 37 statements (LongMethod)

要想保持完美体型就得经常锻炼,而且得科学锻炼,写程序也是一样,要讲求科学,要不断反省自己,另外要经常写,保持肌肉健壮。

MongoDB 东扯西

话说,传统的关系型数据库已经不那么吃香了。近来其他类型的数据库如雨后的春笋一般层出不穷,令人目不暇接,但也肯能非常令人恼火。究其原因,大多是觉得关系型数据库的性能不行,很多情况下也不适合互联网的应用。有针对性的,各路神侠构造了各种兵器,其中很大一类是基于key/value机制的数据库,典型的代表呢,就是Tokyo Cabinetmemcachedb。Tony Bain的一篇文章Is the Relational Database Doomed?对一些数据库发展的现状做了一个客观的评说。另外,还有ibm提出的xml的东西,不知道是个啥,半死不活的样子。很多年以前还有人搞了面向对象的数据库,后来也不怎么着,算是无疾而终(这么说很对不起还活着的兄弟,那能怎么样呢,不过是行尸走肉)。

无疑,数据库战场上的波澜会将每一个互联网开发者卷入其中。一波又一波的“去SQL化”浪潮(详见这里)在改变我们观念的同时,也在改变互联网的构建方式。

Document-Oriented database应运而生,典型代表

CouchDB 有篇不错的英文介绍

不过今天的主角是MongoDB注意啊不是MangoDB,让我们开开心心的开始吧。

Ruby的编码真愁人

我说这么好的语言在国内流行不起来呢。编码是个大问题。不管怎么说Java在这一点上做的还是很有先见之明的。而且做得也算最好了。编码的问题直接影响了我举的例子,本来是想拿blade的豆瓣开涮的,无奈编码问题没有解决。

不过今天还是比较有成绩的,第一,我的RadRails终于能加载nokogiri,究其原因多半是从前先装了RadRails后装的ruby1.8。反复重装几次也不能解决问题,后来把用户数据全部删除,重新关联解释器就好了。真是折腾死我了。

另外大家一定要用nokogiri,暂时不要去想别的解析器了,如果你看到别的教程说Hpricot好,多半儿这个文章是一年以前写的没啥价值了。不要走回头路。

补充一个非常好的学习地址

Railscasts

看演示,学ruby,让ruby在你眼前闪亮!

当然还有相应的文字版本,供那些喜欢安静,或者带宽较小的朋友。

home

Web Scraping in Ruby 直捣黄龙

今天我们来看看怎么获取我们关心的信息,直击要害。

Web给了我们太多不想要的信息,比如说你去新浪看新闻,竟是些不着边际的花边新闻,只不过是些惹眼的词汇罢了,并没有什么真实的内容。我经常有这种感受,为了看网页中的一点点儿东西,要忍受整个网页里面的垃圾信息对我的轰炸,各种广告,弹出对话框,有的还有脚本漏洞真实令人防不胜防,烦不胜烦。

比如我就想知道当下微软的股票价格,我怎么办呢?

http://finance.yahoo.com/q?d=t&s=MSFT

就是一个能提供这种信息的地方,哦那里有股票的价格。

image

我们用一个小工具SelectorGadget,这是个什么东西呢?简单来说,这个工具告诉我们关于某种信息的精确定位坐标。比如说我要找百度大厦,在地图上告诉你“这儿”就是不精确的,但是要是告诉你在“上地九街”就很精确了。这个工具就提供精确制导坐标,你只要点击你想要的区域,它就能自动生成css选择表达式或者xpath表达式,非常高效。有了它,几乎能把90%以上的网页都搞定。

闲言少叙见代码

require 'rubygems'
require 'nokogiri'
require 'open-uri'
 
msft_stock_url = "http://finance.yahoo.com/q?d=t&s=MSFT"
doc = Nokogiri::HTML(open(msft_stock_url))
 
doc.xpath("//*[(@id = \"table1\")]//*[(@id = \"yfs_l10_msft\")]").each do |item|
  puts item.text   
end

Web Scraping in Ruby 漫谈

什么是Web Scraping

http://en.wikipedia.org/wiki/Web_scraping

 

到目前为止,广袤的中文世界里都没有对Web Scraping 的一个定义,所以这里我怎么说怎么有道理,即使是乱放屁,恐怕一时半会儿也不会有人找到头上。

Web Scraping也叫Web Harvesting或者Web data extraction。直译一般不能表达意思的真谛,基本的意思就是Web上有一些我们需要的东东,我们通过一些技术手段把这些东东获取回来。scrap本身是踢出糟粕的意思,很形象,因为web上的绝大部分东西是我们不想要的,是要剔除的。harvest也很形象,我们的目的就是获取有用的数据,就像我们种地是为了获得粮食。extract的抽取,选取的意思表达的就稍微有点学术化了,但其本质还是在说怀有一定目的的从Web获取有用的信息。

那么到这里,我们可以看到,基本的步骤有两个,第一步把信息获取回来(grab),第二步抽取有用的信息(extract)。狭义地讲,Web Scraping就是如何做这两步的技术手段,比如本文题目中的Web Scraping in Ruby,就是说我们用Ruby作为工具的Web Scraping技术。

也有人觉得应该把Web Scraping定义为“超浏览器行为”(beyond browser activity),因为其本质上是一种不用浏览器却模拟了浏览器行为的技术。在传统的unix世界里,若是掌握了grep,sed,awk就牢牢地掌握系统信息的来龙去脉,不过前提是你得知道信息在哪里,毕竟grep,sed,awk的重点在于抽取(extract)。类似的,在广袤无垠的web世界里,尤其是web2.0+的世界里,一种scraping技术就显现出其实用价值了。

是漫无目的地被垃圾淹没还是有条不紊地收集信息?是驱动信息为我服务还是沦为一条信息,悄无声息地幻灭,也许只在一念之间。在传统的单机世界里面,信息的总量是有限的,所以获取信息往往显得不是那么重要。但是Web从某种意义上说是一个无边界的系统,恐怕冒冒失失的想要“慢慢来”,只会进得去出不来。当我们说把整个海洋的水都放到自己船上的时候,我们都觉得好笑,但是很多人却在做这样的事情。

这里要额外说说爬虫(crawler或者spider),爬虫基于简单的原则获取web页面,所以它也是web scraping技术的一部分,但仅仅算作是一个开始。后面的信息处理才是对信息价值的极大提升。后面的部分还会详细说信息的处理的。

总之,web scraping就是从web上获取我们要的信息,但是与我们人类行为有点区别,这里有自动化的,(很可能的)从海量数据中获得的意味,并且一定是剔除了无用垃圾的。

为什么要Web Scraping

因为能赚钱。看多么简单明了,但多么有说服力啊。你若是不往下看,要么你不知道钱的好处,要么是你已经被太多的赚钱谎言欺骗过了。哈哈,我说的是真的,我也是非常认真地说的。

那么我们分为三个类别来说说这项技术如何能赚钱吧。

肯定合法的途径

外汇套利,包括外汇三角套利,期权套利等方式。基本的来说,可以建立一个小程序实时跟踪国际外汇市场的行情,一旦发现套利机会,自动建立头寸(position)。至于细节不便多说,可以查看相关书籍。外汇市场瞬息万变,交易量巨大,广大穷人们还是大有可为空间的。

不一定合法的途径

网页游戏外挂,当然也包括各种网页应用的外挂程序。比如QQ的自动停车,自动偷菜之类的。外挂是能卖钱呢。当然是否涉及到法律风险本身并不是技术要讨论的话题。

比如说发帖机器人,一下子发成百上千的帖子的你真的以为是人啊?

一定不合法的途径

垃圾邮件XXX,如何能获取更多的邮件地址呢,如何能让传统的爬虫更快速,更准确呢。

密码破解器,我知道你知道一大堆工具,John the ripper, hydra等等。但是我肯定你看着各种web手痒痒,你需要新的工具了。

当然了,学术研究啊,商业的其他应用啊,我就不怎么细说了,因为那些不直接来钱。

怎么Web Scraping

哈,这里还是要兜售ruby,其实我不兜售人家销量野蛮高的啊。用文字不太好形容,那么用关键词吧。在初期还是主要讲讲nokogirimechanize的一些应用。可能大家觉得很不屑,觉得这个话题没意思,但是这些技术正是被社区广泛使用,但同时无论英文资料还是中文资料都非常少的。说以我争取做一个好孩子,做一个系统的教程。

至于拿回数据以后,进一步用统计还是人工智能方法来处理挖掘,也会涉及到一些,但毕竟我不是这方面的专家,所以介绍的东西会相对少一些,但会补充相应的资料,供大家提高用。

求和的100种写法

说100是一种夸张,引自《孔乙己》的“茴”字的四种写法

无非是老题目:从1到100加起来是多少。这应该是每个程序员的第二个程序,第一个是“Hello,World!”。这个实例的作用就是告诉大家在一门新的语言里,所谓“循环”究竟该怎么玩,或者说究竟有多少种玩法。

但这回用Ruby写,纯粹发泄。这两天脑残了,看Ruby。非常不理解被大家炒来炒去的概念“Closure”,有那么难么,有那么神奇么。看来没学过lambda和scope的同学真的要补补课啦。我是觉得closure是很自然的事儿,就是没人给我讲我也会那么去干的,简直在自然不过了。

# The first example
sum = 0 
for i in 1..100
  sum += i
end
p sum
 
# The second example
sum = 0
i = 1
while i <= 100 do
  sum += i
  i += 1
end
p sum
 
# The third example
sum = 0
i = 1
until i > 100 do
  sum += i
  i += 1
end
p sum
 
# The fourth example
sum = 0
( 1 .. 100 ).each do | i |
  sum += i
end
p sum
 
# The fifth example
sum = ( 1..100 ).inject( 0 ){| result,i | result + i}
p sum