2008年11月11日星期二

2008年11月10日星期一

Google Chrome浏览器Logo诞生过程(转)


太有才了!

CHM Reader

Firefox 的一个插件,可以在浏览器里打开 *.chm 文件,就像浏览网页一样。

打开 CHM 文件:在工具栏上,文件 -> Open CHM Files
打开左侧目录(书签)栏:查看 -> 侧栏 -> CHM Reader,或 Ctrl + E

2008年10月23日星期四

再设hosts

不知道为什么,以前设的 hosts 不好使了:
72.14.219.190 xxx.blogspot.com
别人的不设也能访问,就是访问不了自己的,难道我人品有问题。

nslookup xxx.blogspot.com

Server: gjjline.bta.net.cn
Address: 202.106.0.20

Non-authoritative answer:
Name: blogspot.l.google.com
Address: 209.85.175.191
Aliases: xxx.blogspot.com


把得到的 IP 再设置到 hosts 里:
209.85.175.191 xxx.blogspot.com
又可以访问了

How To Generate Local RDocs

最近 http://www.ruby-doc.org/core/ 无法访问了,而 ri 用起来始终不习惯。偶然看到了个生成 Local RDocs 的方法。

http://www.mindsifter.com/2008/1/8/generate-local-ruby-core-rdocs
http://weblog.jamisbuck.org/2005/4/8/rdoc-template

Download the template here
To use it, copy it into the “rdoc/generators/template/html” directory of your Ruby installation.
Then:

rdoc --template=jamis --op <some output dir>


可能由于我机器上 rubygem 版本的原因,这个模板用不了,不过可以用自带的模板

rdoc --template=html --op <some output dir>


注:执行这个命令时,要先转到 ruby 源代码的目录,默认是生成该目录下所有文件的 rdoc。
也可以指定文件:

rdoc --template=html --op <some output dir> array.c

2008年7月21日星期一

奇异鸟kiwi(转)

http://www.youtube.com/watch?v=sdUUx5FdySs
很感人的视频,以前汉东推荐过的。

2008年7月8日星期二

浅析Ruby on Rails部署方案(转)

http://docs.google.com/View?docid=ddcvzh74_28f9xppqfh
有数据有分析有结论,图文表并茂,相当强大。

2008年6月29日星期日

人体使用手册 1、2 读书摘要

腾不出时间睡觉的人,迟早会腾出时间来生病。

今天不好好睡觉,明天好好睡长觉。

症现于四肢五官,病存于五脏六腑。

生气的实质意义是,用别人的过错惩罚自己。

-------------------------------------------

中国人是全世界最懂得养生理论的民族,也是全世界最不遵循养生理论的民族。

多数情形怒气的发作都是这种“零存整取”的模式。

2008年6月22日星期日

盖茨要退休了

新闻说盖茨下周就正式退休了,财产全部捐给慈善基金,不留给子女。这一点真让中国的啃老族汗颜。其实啃老,父母的纵容有很大的责任。

虽然很讨厌M$这个公司,但很佩服盖茨这个人。微软最辉煌的时代已经过去了,只是他们曾经领先太多,暂时还没有公司有能力赶超。不过 Google 已经称霸了网络搜索,Apple 也咸鱼翻身越翻越有劲。就技术人员的愿望,当然不希望微软收购雅虎,但是从商业上考虑,微软收购是最好的选择。

IBM, Microsoft, Google, 江湖上的下一个大佬将会是谁呢?Facebook?

人体使用手册

很早以前就下过这本书的电子版了,当时看了一部分,观点比较新颖,其实也就是传统中医的理论++。昨天搜索发现《人体复原工程》(人体使用手册2)出版了,于是去卓越订了这两本,明后天应该就送到了。

以前还买过《身体使用手册》,也是1、2两本,一本红的一本蓝的,是美国医生写的,也很不错。

顺便提下,以后是不会在淘宝买书了。网购不是图便宜,而是图方便。但是快递一点也不会给你方便。至少卓越会提供“只周末送货”“只工作日送货”的选择(上次在淘宝买了《Restful Web Services 中文版》后,才发现该书的官网也提供这种选择),对于整天上班的人来说是很贴心的服务。淘宝的个体商户发快递就不会考虑到这一点,好像我们是不用做事整天呆在家里等快递一样。

《人体使用手册》的作者在blogspot的博客,在hexun的博客。估计也是体谅水生火热的大陆读者访问不了blogspot,所以开了两个博客。

再顺便提下,好像blogspot又解封了,没有设置hosts也能访问了。

郑智换成郑智化(转)

其实把郑智换成郑智化,中国队水平也不会受影响!
其实把李铁换成李铁拐,中国队水平也不会受影响!
其实把李雷雷换成邦弗雷雷,中国队水平也不会受影响!
其实把董方卓换成董卓,中国队水平也不受影响!
其实把吴承英换成吴承恩,中国队水平也不会受影响!
其实把姜坤换成姜昆,中国队水平也不受影响!
其实周海滨换成周海媚,中国队水平也不会受影响!
其实把徐云龙换成李云龙,中国队水平也不会受影响!
其实把马明宇换成马天宇,中国队水平也不会受影响!
其实把韩鹏换成韩红,中国队水平也不会受影响!
其实把杜镇宇换成吴镇宇,中国队水平也不会受影响!
其实把足球换成西瓜,中国足球水平也不会受到影响!

《Python 核心编程》第二版 要出版了

是社区组织群体翻译的。社区翻译的好处是进度快,人多力量大嘛;坏处是:不容易组织,不容易保证翻译质量。尤其是这么大部头的一本,能够圆满完成实属不易。

然而,听说大家的劳动成果居然被窃取了?
http://nicholas-ding.javaeye.com/blog/206347

如果是真的,那大家就不要去买纸版的了吧。
这个是翻译项目的 SVN
http://openbookproject.googlecode.com/svn/trunk/CorePython_zh/
大家用 TortoiseSVN 把它 Export 出来就可以了。

不是教唆大家盗版,实在是没有必要便宜了那个窃取劳动果实的家伙。

2008年6月21日星期六

开始使用Safari

Firefox 3 刚出来的时候,着实兴奋了一阵。然而经过几天的试用,发现还是很吃内存,经常假死。主要是我机器配置实在是太低了,在公司的机器上,即使在虚拟机上跑都还是很爽。没办法,下了个 Safari 试试先。速度还可以,也没那么吃内存。假死过一次,希望是偶然。

也有不爽的地方。标签一溜的黑色,看着很费眼睛。也没有 Firefox 那么多的插件。有些操作习惯、快捷键也有不同。

总之,Safari 和 Firefox 都用,各有其好用的一面。

这篇 Blog 就是在 Safari 下写的。

RESTful Web Services 中文版 读书摘要 第五章

P109
统一接口意味着,在面向对象设计里被视为动词的事务,在面向资源的设计里必须被视为对象。在面向资源的架构里,一个读者不能“订阅”一个栏目,因为“订阅”不属于统一接口里的方法。必须通过另一个订阅对象来代表读者与栏目之间的关系。
如果要给我的资源增添新方法,就要为此定义一个新资源。
----------------------------
The uniform interface means that a resource-oriented design must treat as objects what an object-oriented design might consider verbs. In the ROA, a Reader can’t subscribe to a regularly appearing Column, because “subscribe to” is not part of the uniform interface. here must be a third object, Subscription, representing that relationship between a Reader nd a Column.
Whenever I’m tempted to add a new method to one of my resource “classes,” I’ll resolve the problem by defining a new kind of resource.

P109
设计资源的步骤:
(注:这里中英文没有一一对应)
1. 规划数据集
2. 把数据集划分为资源
对于其中每种资源:
3. 用 URI 为该资源命名
4. 设计发给客户端的表示
5. 用超链接和表单把该资源与已有资源联系起来
6. 考虑有哪些典型的事件经过
7. 考虑可能出现哪些错误情况
----------------------------
1. Figure out the data set
2. Split the data set into resources
For each kind of resource:
3. Name the resources with URIs
4. Expose a subset of the uniform interface
5. Design the representation(s) accepted from the client
6. Design the representation(s) served to the client
7. Integrate this resource into existing resources, using hypermedia links and forms
8. Consider the typical course of events: what’s supposed to happen?
9. Consider error conditions: what might go wrong?

P112
一个资源就是任何值得作为超链接目标的事物
----------------------------
Remember that a resource is anything interesting enough to be the target of a hypertext link.

P113
服务暴露的资源可以分为三类:
为特别目的专门预定义的一次性资源
服务暴露的每一个对象所对应的资源
代表在数据集上执行算法的结果的资源
----------------------------
Web services commonly expose three kinds of resources:
Predefined one-off resources for especially important aspects of the application.
A resource for every object exposed through the service.
Resources representing the results of algorithms applied to the data set.

P117
要理解“把算法暴露为资源”这一方式,是需要一些时间的。你不要从动作(如“在地图上搜索地点”)方面来考虑,而要从该动作的结果方面来考虑(如“地图上符合搜索条件的地点”)。
----------------------------
It takes a while to get the hang of exposing an algorithm as a set of resources. Instead of thinking in terms of actions (“do a search for places on the map”), you need to think in terms of the results of that action (“the list of places on the map matching a search criteria”).

P117
根据集体经验,URI 设计有三条基本原则:
1. 用路径变量来表达层次结构: /parent/child
2. 在路径变量里加入标点符号,以消除误解: /parent/child1;child2
3. 用查询变量来表达算法的输入,例如: /search?q=jellyfish&start=20
----------------------------
There are three basic rules for URI design, born of collective experience:
1. Use path variables to encode hierarchy: /parent/child
2. Put punctuation characters in path variables to avoid implying hierarchy where none exists: /parent/child1;child2
3. Use query variables to imply inputs into an algorithm, for example: /search?q=jellyfish&start=20

P118
对于可按层次结构组织的作用域信息,采用路径变量是最好的方式。
----------------------------
Path variables are the best way to organize scoping information that can be arranged hierarchically.

P119
我建议:当作用域信息的次序有关紧要时,就用逗号,否则就用分号。
----------------------------
I recommend using commas when the order of the scoping information is important, and semicolons when the order doesn’t matter.

P129
表示传达资源状态,但它不必传达资源的全部状态,它只要传达部分状态就行了。
你可以通过跟随表示里的链接得到更多状态。
----------------------------
A representation conveys the state of its resource, but it doesn’t have to convey the entire state of the resource.
You can get as much of that state as you want by following its links to other resources.

P132
假如只能从本书得到一样东西,我希望那是你对“Web 服务就是为机器设计的网站”这一观念的理解(假若之前还没有的话)。
----------------------------
If you get only one thing out of this book, I hope it’s that this idea starts seeming natural to you (assuming it didn’t before).

P138
条件 HTTP GET 可以节省客户端和服务器的时间与带宽。它是通过两个响应报头(Last-Modified 和 ETag)和两个请求报头(IfModified-Since 和If-None-Match)实现的。
----------------------------
Conditional HTTP GET saves client and server time and bandwidth. It’s implemented with two response headers (Last-Modified and ETag), and two request headers (If- Modified-Since and If-None-Match).

P138
有些数据也许并不经常变化。
大部分时候,客户端对一个资源的第二次(及以后各次)HTTP 请求都是不必的。其实它们完全可以重用上一次请求时获得的表示。
----------------------------
This data is not constantly changing.
Most of the time, the client’s second and subsequent HTTP requests for a resource are wasted. They could have just reused the representation from their first request.

P138
每当服务器返回一个表示时,服务器应该为 Last-Modified 报头设置一个时间值,表示数据最后更新的时间。
客户端可以把这个最后更新时间保存下来,供以后使用。
当该客户端再次向同一个资源发送 GET 请求时,可以在 IfModified-Since 报头里提供那个最后更新时间。
假如资源的数据刚好在该客户端的两次请求之间发生了变化,服务器会返回响应代码 200(“OK”),并在实体主体里提供最新的表示——这就跟一次正常的 HTTP 请求一样。但是,假如数据没有变化,服务器将返回响应代码 304(“Not Modified”),并省去实体主体。这样,客户端就知道可以重用已缓存的表示了(因为数据没有改变)。
----------------------------
Whenever a server serves a representation, it should include a time value for the Last-Modified HTTP header. This is the last time the data underlying the representation was changed.
The client can store this value of Last-Modified and use it later.
The second time the client makes a GET request for that resource, it can provide that time in the If-Modified-Since header.
If the underlying data changed between the two requests, the server sends a response code of 200 (“OK”) and provides the new representation in the entity-body. That’s the same thing that happens during a normal HTTP request. But if the underlying data has not changed, the server sends a response code of 304 (“Not Modified”), and omits any entity-body. Then the client knows it’s okay to reuse its cached representation: the underlying data hasn’t changed since the first request.

P139
我还需要为不能处理的请求作考虑。假如遇到出错的情况,我会返回一个在 3xx、4xx、5xx 范围内的响应代码,并在 HTTP 报头里给出补充信息。假如响应包含实体主体,那肯定是一个描述错误状态的文档,而不是所请求资源的一个表示。
----------------------------
I also need to plan for requests I can’t fulfill. When I hit an error condition I’ll send a response code in the 3xx, 4xx, or 5xx range, and I may provide supplementary data in HTTP headers. If they provide an entity-body, it’ll be a document describing an error condition, not a representation of the requested resource (which, after all, couldn’t be served).

2008年6月19日星期四

FileUtils

FileUtils 是 ruby 的一个标准库。所提供的部分功能 核心库的 File 类也提供了,但更为强大和方便。
比如删除文件操作

File.delete(file_name)
FileUtils.rm(list, options)

强制删除

FileUtils.rm('NotExistFile', :force => true) # never raises exception

如果是File.delete的话,恐怕还得先判断一下:

File.delete('file_name') if File.exist?(file_name)

Vi 跳到文件第一行、最后一行

:$ 跳到文件最后一行
:0或:1 跳到文件第一行

== 2008-06-24

gg 跳到文件第一行
Shift + g 跳到文件最后一行
感谢Ryan

Google Reader keyboard shortcuts

http://www.google.com/support/reader/bin/answer.py?answer=69973&topic=12012
有一些快捷键在 Google Groups 也能用。真是太方便了。

2008年6月18日星期三

QQ空间的RSS

空间RSS订阅地址:
http://feeds.qzone.qq.com/cgi-bin/cgi_rss_out?uin=QQ号码
省得每次还要打开巨慢巨垃圾的QQ和IE。

不过貌似还是有问题:并没有把所有文章都列出来,而是毫无规律地选了些,而且更新时间延迟以月来计。不知道tencent出于什么考虑,还是说技术上的问题。不管是哪种原因,都给人足够的理由鄙视之。

ssh key

http://github.com/guides/providing-your-ssh-key
就是如何生成你的ssh key。其实很简单,以前在别人指导下弄过,后来忘了。这回搞 github 提交要用到。

du

显示目录或文件的大小。就是 disc usage 的意思。

du /home/linjian/doc

2008年6月17日星期二

grep统计某一字符串出现的次数

grep的功能很强大。
比如我们要统计下面这个字符串在某一文件里出现的次数。
<VideoDocument video_id="asdfwer3d">

grep -Ec '<VideoDocument.+>' /home/linjian/doc/test.xml

其中参数E表示元字符扩展。因为正则表达式里用了+,必须加参数E。参数c表示count,就是输出匹配次数。
关于grep的一些参考:http://man.chinaunix.net/newsoft/grep/open.htm

============ 2008-11-10 添加 ============
上述方法适用于统计单个文件,如果要统计多个文件,则可以:

grep -o 'UPDATE' *.sql | wc -l

单独运行一个RSpec测试

spec spec/lib/xx_spec.rb -s 'description ...'

其中description ... 是describe 'xxx' do 里的xxx加上 it 'should xxx' do 里的字符串。

Deep Profiling jQuery Apps

http://www.javaeye.com/news/2611
纯备忘。

Gmail客户端详细架构

Gmail客户端详细架构之一
Gmail客户端详细架构之二
To Be Continue ...

faster-xml-simple

Ruby的标准库里有一套XML的API,叫作REXML
有个xml-simple的gem,可以很方便地把xml转换成hash。它调用的是REXML,是用DOM方式解析的XML。
用起来是很方便,但不幸的是,DOM方式太慢了,而且太耗内存了。文件小不太明显,遇上个几十M的,就直接把内存吃光了,躺在那打饱嗝,也不工作。

好在还有个gem叫faster-xml-simple,哈哈,听名字就是跟xml-simple叫上劲了。它也可以把xml转换成hash,使用起来跟xml-simple一样,但是功能少了挺多,后面我们会来hack它。
faster-xml-simple也是用DOM方式解析XML的,只不过它调用的是libxml-ruby。后者大部分代码都是用c写的,当然快了很多,内存也节约了不少。其实libxml-ruby就是ruby语言跟libxml2的綁定(The Libxml-Ruby project provides Ruby language bindings for the GNOME Libxml2 XML toolkit.)。

那么faster-xml-simple跟xml-simple相比少了些什么呢?你可以看看它主页上列出的 issues。下面要讲讲我自己发现的问题。
不支持grouptags。
对CDATA的支持有问题。
options的key要小写(如grouptags),而xml-simple是大小写混合的(如GroupTags)。
事实上,faster-xml-simple只有三个默认参数,见源代码:
def default_options
{'contentkey' => '__content__', 'forcearray' => [], 'keeproot'=>true}
end
除此之外,还支持'suppressempty', 'forcecontent' 这两个参数。

下面是我hack的代码,支持了grouptags,支持了CDATA,顺便将多个空格压缩为一个空格(调用了String#squeeze!方法)。用法:调用xml_in方法时,传入参数'compress_whitespace' => %w(item, tag, node),相应item等元素的内容里,多个空格就会被压缩为一个空格。
所谓hack,其实就是把xml-simple里相应功能的代码搬过来,然后再抄抄REXML的代码。那个CDATA的hack,纯粹就是帮faster-xml-simple改了个bug。
class FasterXmlSimple
private
# Support CDATA
def text_node?(element)
!element.text? && element.all? {|c| c.cdata? || c.text?}
end

def compress_whitespace?(ele_name)
@options.has_key?('compress_whitespace') &&
@options['compress_whitespace'].include?(ele_name)
end

def collapse(element)
result = hash_of_attributes(element)
if text_node? element
text = collapse_text(element)
text.squeeze!(" \n\t") if compress_whitespace?(element.name)
result[content_key] = text if text =~ /\S/
elsif element.children?
element.inject(result) do |hash, child|
unless child.text?
child_result = collapse(child)
(hash[child.name] ||= []) << child_result
end
hash
end
end
if result.empty?
return empty_element
end

# Compact them to ensure it complies with the user's requests
inline_single_element_arrays(result)
remove_empty_elements(result) if suppress_empty?

# Disintermediate grouped tags.
if @options.has_key?('grouptags')
result.each do |key, value|
next unless (value.instance_of?(Hash) && (value.size == 1))
child_key, child_value = value.to_a[0]
if @options['grouptags'][key] == child_key
result[key] = child_value
end
end
end

if content_only?(result) && !force_content?
result[content_key]
else
result
end
end
end


== 2008-06-27
做了一点修改,原先 xml 里有注释的话,在 parse 后的 hash 里会有一个讨厌的 'comment' => {} (大致是这个吧,记不太清了)
在某些情况下会破坏 grouptags 的作用,于是把 comment 去掉了。
将 collapse 方法里的这一行
(hash[child.name] ||= []) << child_result
改为
(hash[child.name] ||= []) << child_result unless child.comment?
就行了

BTW:Dongbin 同学把修改后的 faster-xml-simple 放在了 github 上,地址为:http://github.com/dongbin/faster-xml-simple/tree/master

Linux下查看内存情况

free

或者

top d 1

其中1表示每过1秒刷新一次。

2008年6月16日星期一

tcpdump

sudo tcpdump -X -i eth0 -s 1024 host localhost and port 3000

const missing

用const_missing , const_set , const_get 来自动生成常量。
这三个方法都属于Module类的方法。

示例代码如下。其中Permission是个Model类。
module Perm
require "permission"
# lazy load consts
def self.const_missing(name)
if @perms.nil?
@perms = Permission.find(:all).map{|p|p.name}
@perms.each do |perm|
Perm.const_set( perm.gsub(/\./,'_').upcase , perm)
end
end
return nil if !const_defined?(name)
Perm.const_get(name)
end
end

config my logger

config/environment.rb:
$my_logger = Logger.new("#{RAILS_ROOT}/log/my.log")
其实感觉放在config/environments/development.rb中更好,毕竟一般的调试信息只在开发时需要。

controller:
$my_logger.info("hello")

打印到控制台:
Logger.new(STDOUT).info("display in the console")
打印到开发日志文件:
logger.info("display in the development.log")

enum 对应 symbol 类型

MySQL数据库字段是enum类型的话,对应的activerecorder对象属性是Symbol类型的。

2008年6月15日星期日

RESTful Web Services 中文版 读书摘要 第四章

P81
任何事物,只要具有被引用的必要,它就是一个资源。
----------------------------
A resource is anything that’s important enough to be referenced as a thing in itself.

P81
是什么令资源称得上是一个资源?它必须至少有一个 URI。URI 既是资源的名称,也是资源的地址。如果一则信息没有 URI ,它就不能算是一个资源,也不能算真正在 Web 上,而只能算作描述另一个资源的一些数据。
----------------------------
What makes a resource a resource? It has to have at least one URI. The URI is the name and address of a resource. If a piece of information doesn’t have a URI, it’s not a resource and it’s not really on the Web, except as a bit of data describing some other resource.

P86
在最终用户看来,Gmail 的 URI 始终是 https://mail.google.com/。无论作什么操作,无论从 Gmail 获取什么信息或向 Gmail 上传什么信息,你不会看到别的 URI。
Gmail有一个可寻址的版本,位于 URI https://mail.google.com/mail/?ui=html。
Gmail Web 服务是可寻址的,不过调用该服务的 Gmail Web 应用不是可寻址的。
----------------------------
From the end-user perspective, there is only one Gmail URI: https://mail.google.com/. Whatever you do, whatever pieces of information you retrieve from or upload to Gmail, you’ll never see a different URI.
Compare the Ajax interface against the more addressable version of Gmail you get by starting off at the URI https://mail.google.com/mail/?ui=html.
The Gmail web service is addressable, but the Gmail web application that uses it is not.

P90
无状态性意味着:有一种状态,是服务器不应该保存的。实际上,状态分为两种。从现在开始,我将区分应用状态与资源状态:前者应该保存在客户端,后者应该保存在服务端。
----------------------------
Statelessness implies there’s only one kind of state and that the server should go without it. Actually, there are two kinds of state. From this point on in the book I’m going to distinguish between application state, which ought to live on the client, and resource state, which ought to live on the server.

P90
每当客户端向服务器发请求时,请求里必须包含服务器处理该请求所需的所有应用状态。
各个客户端应当管理自己的应用状态。
----------------------------
This means that whenever a client makes a request, it must include all the application states the server will need to process it.
The client should be in charge of managing its own path through the application.

P95
将超媒体作为应用状态的引擎。
客户端应用状态在服务器提供的超媒体(即超文本表示里的链接和表单)的指引下发生变迁。
----------------------------
Hypermedia as the engine of application state.
The server guides the client’s path by serving “hypermedia”: links and forms inside hypertext representations.

P97
HTTP四种基本方法:
获取资源的一个表示:HTTP GET
创建一个新资源:向一个新 URI 发送 HTTP PUT,或者向一个已有 URI 发送 HTTP POST
修改已有资源:向已有 URI 发送 HTTP PUT
删除已有资源:HTTP DELETE
----------------------------
Retrieve a representation of a resource: HTTP GET
Create a new resource: HTTP PUT to a new URI, or HTTP POST to an existing URI
Modify an existing resource: HTTP PUT to an existing URI
Delete an existing resource: HTTP DELETE

P98
HEAD 请求就是比 GET 请求少返回实体主体而已。
----------------------------
HEAD gives you exactly what a GET request would give you, but without the entity-body.

P99
在一个 REST 式设计中,POST 通常被用于创建从属资源——即从属于“父”资源(另一个资源)而存在的资源。
----------------------------
In a RESTful design, POST is commonly used to create subordinate resources: resources that exist in relation to some other “parent” resource.

P99
PUT 与 POST 的区别就在于:假如是客户端负责决定新资源采用什么 URI,那就用 PUT;假如是服务器负责新资源采用什么 URI,那就用 POST。
----------------------------
The difference between PUT and POST is this: the client uses PUT when it’s in charge of deciding which URI the new resource should have. The client uses POST when the server is in charge of deciding which URI the new resource should have.

P100
给一个资源发 POST 请求,未必总是创建一个全新的从属资源。有时,当向一个资源 POST 数据时,它把你发来的信息附加到它自身的状态上,而不是用该信息新建一个资源。
----------------------------
The information conveyed in a POST to a resource doesn’t have to result in a whole new subordinate resource. Sometimes when you POST data to a resource, it appends the information you POSTed to its own state, instead of putting it in a new resource.

P102
假如对一个资源做某操作是幂等的,那么无论对该资源做多少次这种操作,结果总是一样的;对该资源的第二次及其后各次操作,不会令该资源的状态产生比第一次操作更多的影响。
----------------------------
an operation on a resource is idempotent if making one request is the same as making a series of identical requests. The second and subsequent requests leave the resource state in exactly the same state as the first request did.

P103
幂等性对实践提出的要求是:不要允许客户端对资源状态做相对的改动。比如一个资源把某个数值作为其部分资源状态来保存,客户端可以通过 PUT 操作把该数值设为 4、0 或 -50,但是它不应通过 PUT 操作对该值作“加一”操作。
----------------------------
The practical upshot of this is that you shouldn’t allow your clients to PUT representations that change a resource’s state in relative terms. If a resource keeps a numeric value as part of its resource state, a client might use PUT to set that value to 4, or 0, or -50, but not to increment that value by 1.

P103
POST 既不是安全的,也不是幂等的。
----------------------------
POST is neither safe nor idempotent.

RESTful Web Services 中文版 读书摘要 第二章

P28
服务器应该是理想主义的;客户端必须是实用主义的——这是 Postel 法则“严以律己,宽以待人”的一个变形。
----------------------------
Servers should be idealistic; clients must be pragmatic. This is a variant of Postel’s Law: “Be conservative in what you do; be liberal in which you accept from others.”

P47
JSON 适用于表达数据结构;而 Web 提供的主要是文档——一种不规则的、自描述的、相互链接的数据结构。XML 和 HTML 专门用于表达文档。
----------------------------
JSON is good for representing data structures in general, and the Web mainly serves documents: irregular, self-describing data structures that link to each other. XML and HTML are specialized for representing documents.

RESTful Web Services 中文版 读书摘要 第一章

P13
REST式架构意味着,方法信息都在HTTP方法里;面向资源的架构意味着,作用域信息都在URI里。
----------------------------
In RESTful architectures, the method information goes into the HTTP method. In Resource-Oriented Architectures, the scoping information goes into the URI.

P17
Flickr web API 是一种 REST-RPC 混合架构:当客户端通过GET方法获取数据时,它是REST式的;当客户端通过GET方法修改数据时,它是RPC式的。
----------------------------
The Flickr web API is a REST-RPC hybrid: RESTful when the client is retrieving data through GET, RPC-style when the client is modifying the data set.

P18
一个 REST 式面向资源的服务为客户端可能操作的每一则数据暴露一个 URI;一个 REST-RPC 混合服务,为客户端可能进行的每一个操作暴露一个 URI(比如获取数据用一个 URI,删除数据用另一个 URI);一个 RPC 式服务,为每个处理远程调用的进程暴露一个URI,一般来说这样的URI只有一个,即服务的“端点”。
----------------------------
A RESTful, resource-oriented service exposes a URI for every piece of data the client might want to operate on. A REST-RPC hybrid exposes a URI for every operation the client might perform: one URI to fetch a piece of data, a different URI to delete that same data. An RPC-style service exposes one URI for every processes capable of handling Remote Procedure Calls (RPC). There’s usually only one such URI: the service “endpoint.”

P20
REST 式架构的主要竞争对手是 RPC 式架构,而不是 SOAP 这样特定的技术。没错,几乎所有现有的基于 SOAP 的服务都属于 RPC 式架构,但 SOAP 跟 HTTP 一样,只是一种把文档放在信封里的方式。
----------------------------
The primary competitors to RESTful architectures are RPC architectures, not specific technologies like SOAP. It is true that basically every SOAP service that now exists has an RPC architecture, but SOAP is just a way of putting a document in an envelope with stickers on it, like HTTP.

MySQL查看建表sql

show create table xxx(table_name)\G;

三元表达式 VS &&

无废话,下面两种写法是等价的:
self.user_id = new_user.nil? ? nil : new_user.id
self.user_id = new_user && new_user.id

"Please select"

下拉框里,要出现"Please select"的话,加上":prompt => true"选项:
<%= form.collection_select(:country_id, @countries, :id, :description, {:prompt  => true} %>

2008年6月13日星期五

ps command

查询进程的命令,地球人都知道,就是我不知道。
比如,查ruby进程:

ps aux|grep ruby

2008年6月12日星期四

flazx

http://www.flazx.com/
一个可以下载电子书的网站,纯链接式备忘。

2008年6月11日星期三

vi下查找匹配的字符串

正向查找(就是向下查找)

/some_string

按n查找下一个

反向查找(就是向上查找)

?another_string

用mocha模拟多次调用同一个方法

标题起得比较绕口,看代码,无废话:
argu = [nil, nil, nil, nil, nil, nil, "s", nil, nil, nil, nil]
Something.expects(:a_method).times(argu.size).returns(*argu)
Something的a_method方法会被调用argu.size次,每次分别返回值是nil, nil, ..., "s", nil, ...

2008年6月10日星期二

生成\删除文件

Ruby中,生成一个文件:
Kernel::open(file_name, 'w')
删除一个文件:
File.unlink(file_name)
or
File.delete(file_name)

鉴定完毕

鉴定完毕
Case closed
——《RESTful Web Services》

哈哈,看中文版的时候看到这个“鉴定完毕”笑翻了,马上去找原版看英文是怎么说的。
可以翻译为结案或者收队吗?(警匪片看多了吗?)

查看文件有多少行

cat report.log | wc -l

抢楼,100

哈哈,灌了100层了,纯水,楼下继续。

Free Rails 2.1 Book

http://weblog.rubyonrails.com/2008/6/10/free-rails-2-1-book
一本介绍Rails 2.1新特性的书,原作是葡萄牙语写的,这里可以下载英文版的。

== 2008-06-18
不得不叹服Rails社区的热情,中文版马上就要出来了,从开始组织翻译到完成,不到一周,领取章节都在抢了。
http://chinaonrails.com/topic/view/1713.html

== 2008-06-20
http://chinaonrails.com/topic/view/1754.html
中文版已经出来了,点击这里下载,需要先注册。

2008年6月9日星期一

truncate(截取字符串)

ActionView::Helpers::TextHelper#truncate(text, length = 30, truncate_string = "...")
截取过长字符串,省略部分用truncate_string来代替(默认是...)。

If text is longer than length, text will be truncated to the length of length (defaults to 30) and the last characters will be replaced with the truncate_string (defaults to "…").

Examples

truncate("Once upon a time in a world far far away", 14)
# => Once upon a...

truncate("Once upon a time in a world far far away")
# => Once upon a time in a world f...

truncate("And they found that many people were sleeping better.", 25, "(clipped)")
# => And they found that many (clipped)

truncate("And they found that many people were sleeping better.", 15, "... (continued)")
# => And they found... (continued)


length = 显示的字符串长度 + truncate_string的长度,也就是说,设置length为10,实际显示的字符数是7个,还有3个是用来显示...的。
当然,如果结束符用其他的(比如......),那实际显示的字符串长度就是3个了。

源代码:
def truncate(text, length = 30, truncate_string = "...")
if text.nil? then return end
l = length - truncate_string.chars.length
(text.chars.length > length ? text.chars[0...l] + truncate_string : text).to_s
end
length的长度必须要大于truncate_string的长度,如有必要,在调用truncate方法前我们应该自己先判断一下。
这个截取方法对于中英文字符都有效,因为chars方法返回的是UTF-8的结果。

不过这样也带来了另一个问题,就是截取同样多的字符时,中文比英文显得要长一些:

truncate("Once upon a time in a world far far away", 14)
# => Once upon a...
truncate("这是一串很长很长的中文字符", 14)
# => 这是一串很长很长的中文...

因为程序截取是按照字符个数来截取,但是中文显示时,一个中文会占据两个英文的宽度。

javaeye有人出了一道Quiz来讨论怎么解决这个问题。

2008年6月7日星期六

instance_variable_set

instance_variable_set("@asset_group_#{type.to_s.downcase}", asset_group)
instance_variable_set是Object的方法,ruby核心库提供的功能。
意思是生成一个实例变量,变量的名字是由参数传进来的。

启动scim

scim和java有冲突,所以大多数时候都要把scim关掉。再次启动scim,只需:Alt + F2,然后在弹出框里输入:scim

2008年6月5日星期四

在子类中调用父类的非同名方法

class A
def a
puts "base a"
end
end

class B < A
def b
puts "sub b"
# 调用A的方法a
A.instance_method('a').bind(self).call
end
end
http://rc.org.cn/forums/viewthread.php?tid=879
感谢汉东(blackanger

2008年6月4日星期三

Simian

http://www.redhillconsulting.com.au/products/simian/
用于检查冗余代码的,纯备忘。

学ruby要从娃娃抓起(转中转)





http://msdn.javaeye.com/blog/108220
因为引用的链接里也标明是转的,所以叫转中转。
我当年看的第一本Ruby相关的书就是这本啊。当时对Ruby根本没有一点概念,更加没有听说过Rails,去书店找了半天,一本Ruby的书都没找到,然后去前台查询,只查到了这本书(因为只有这本书的书名带了Ruby,其他的都只带了Rails),然后书店管理员又去找了半天,在Java分类里找到了。当时就觉得很寒。

RESTful Web Services 中文版 - Preface

读完了样章的前言,发现了这样一段有趣的文字:

除非为了示范特定的框架或语言,否则我们将采用Ruby(http://www.ruby-lang.org)语言来描述。
我们选用Ruby是因为:即使对于不懂Ruby的程序员来说,它也是简洁易读的。(因为这是一门很好的语言,而且它的名字跟Sam的姓有着令人困惑的联系。)Ruby的标准Web框架——Ruby on Rails——也是最主要的一种REST式Web服务实现平台之一。如果你不懂Ruby也没关系,我们在代码中嵌入了许多有助于理解Ruby语法的注释。
----------------------------
But whenever we’re not demonstrating a specific framework or language, we use Ruby (http://www.ruby-lang.org/) as our implementation language.
We chose Ruby because it’s concise and easy to read, even for programmers who don’t know the language. (And because it’s nice and confusing in conjunction with Sam’s last name.) Ruby’s standard web framework, Ruby on Rails, is also one of the leading implementation platforms for RESTful web services. If you don’t know Ruby, don’t worry: we include lots of comments explaining Ruby-specific idioms.

这个Sam应该就是Agile Web Development with Rails, Third Edition新加入的作者,当时我就在想怎么会有这么巧的事呢,呵呵。

RESTful Web Services中文版

最先是从InfoQ中文站(here)上看到的介绍。
这是中文版的官网:http://www.restfulwebservices.cn/
去china-pub(here)上看了下,标的是5月出版,貌似刚刚上架。
样章读了一点,翻译得还不错。去淘宝订了一本,居然50都不到(原价70),真便宜啊,都不知道他从什么渠道进的货(盗版无论如何是出不了这么快的),能赚多少钱。
订完后又去官网上看了看,结果发现官网上订是免运费的,算下来和在淘宝上订花的钱是一样多。
本来还想订一本Everyday Scripting with Ruby 中文版(也是最近新出的,以前看过一点英文版的),终于还是忍住了,拒绝当收藏狂。
从china-pub和w3china.org下的样章,打开后顶上赫然挺立着“Microsoft Word - xx.doc”的字样,而从InfoQ中文站下的样章就没有,我想这就是InfoQ更细心一点点,更专业一点点的体现吧。
---------------------------------
找了个英文版的下载。
来源:http://www.matrix.org.cn/thread.shtml?topicId=a5f22fb0-61a6-11dc-acc8-0fa2717adfd4&forumId=29&fid=29

Firefox 3 Download Day

http://www.spreadfirefox.com/en-US/worldrecord/
不待见任何宣传技巧,只是被Firefox 2 的内存泄漏问题折磨得太久了,所谓(对旧的东西)失望越大,(对新的东西)希望就越大。
盼望着,盼望着,多少天过去了,FF3的脚步近了。
官方宣布的时间是"Download Day starts on June 17th at 10 a.m. PDT."
换算成北京时间是2008-6-18 1:00 AM

== 2008-06-19

居然贡献了3次下载量,在公司Linux、Windows各一次,回到家又是一次。
中国的绝对下载量还是不少了,只是考虑到人口基数,一下就摊得很薄了。
北朝鲜的下载量为0。
让我很吃惊的是,伊朗的下载量居然那么大,是中国的1.5倍。
美国还是毫无悬念地排在第一位,贡献了三分之一的下载量。
非洲板块南非一只独秀。

性能方面并没有体会到速度快了很多,可能是我的机器配置太低了吧。内存泄漏的问题好很多了。

什么是欣欣向荣

Customers are signing and we are deploying/developing.
一种理想状态,我们为之努力ing

2008年6月3日星期二

HasFinder

http://pivots.pivotallabs.com/users/nick/blog/articles/284-hasfinder-it-s-now-easier-than-ever-to-create-complex-re-usable-sql-queries
纯备忘。

Sams Teach Yourself Emacs in 24 Hours(转)

http://docs.huihoo.com/homepage/shredderyin/emacs24/index.htm
打包下载
例子elisp包下载
转自:http://docs.huihoo.com/homepage/shredderyin/emacs_doc.html

emacs vi 坑(转)



转自http://124.16.137.72/blog/linux/emacs_vi_坑.html
没想到宣强只用emacs,我以前一直以为他用的是vi,据dongbin说他还是水木emacs版的版主,强啊。

ActiveResource 源码 -- format

我机器上 ActiveResource gem 包的路径:
/var/lib/gems/1.8/gems/activeresource-2.0.2-/

跟format相关的文件有三个
lib/active_resource/formats.rb
lib/active_resource/formats/json_format.rb
lib/active_resource/formats/xml_format.rb

formats.rb
module ActiveResource
module Formats
# Lookup the format class from a mime type reference symbol. Example:
#
# ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
# ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
def self.[](mime_type_reference)
ActiveResource::Formats.const_get(mime_type_reference.to_s.camelize + "Format")
end
end
end

require 'active_resource/formats/xml_format'
require 'active_resource/formats/json_format'
可以看到,format.rb其实是对另外两个文件的统一包装。
其中的const_get方法比较有意思。const_get方法是Module类提供的方法,意思是取Mudule中某常量的值。
ActiveResource::Formats.const_get()返回的是一个Module类。
我想这说明了两点:常量的值可以是任何对象。Module中嵌套的Module名也是常量,Module名是对Module类的引用。

json_format.rb和xml_format.rb没有太多好说的,都是调用ActiveSupport里的to_xxx和from_xxx方法。
xml_format.rb里的decode方法值得一提:
def decode(xml)
from_xml_data(Hash.from_xml(xml))
end

private
# Manipulate from_xml Hash, because xml_simple is not exactly what we
# want for ActiveResource.
def from_xml_data(data)
if data.is_a?(Hash) && data.keys.size == 1
data.values.first
else
data
end
end
可以看到,在decode方法里又调用了一次from_xml_data方法,为什么要这样呢?
因为ActiveSupport里Hash的from_xml方法是调用xml_simple的方法来实现的,而这个xml_simple返回的Hash往往在最外面还包了一层:

{"records"=>[{"name"=>"Matz", "id"=>1}, {"name"=>"David", "id"=>2}]}
{"hash"=>{"name"=>"David", "id"=>2}}

而实际上我们要的是里面的value:

[{"name"=>"Matz", "id"=>1}, {"name"=>"David", "id"=>2}]
{"name"=>"David", "id"=>2}

所以要用from_xml_data把数据再剥开一次。

ActiveSupport里Hash的from_xml方法定义如下(在activesupport/lib/active_support/core_ext/hash/conversions.rb里):
def from_xml(xml)
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
undasherize_keys(typecast_xml_value(XmlSimple.xml_in(xml,
'forcearray' => false,
'forcecontent' => true,
'keeproot' => true,
'contentkey' => '__content__')
))
end
哈哈,DHH自己都说了要Refactor这个方法,不过估计也就只写了个TODO在这里,一直没Refactor过。

另外有个问题,为什么这里可以直接调用XmlSimple的方法呢?
首先要在文件里require一下:
require 'xml_simple'
其次在vendor目录下已经引入了XmlSimple:
activesupport/lib/active_support/vendor/xml_simple.rb

没错,XmlSimple就只有这样一个文件,不信你去把gem安装的XmlSimple打开来看吧,在我机器上的路径是:
/var/lib/gems/1.8/gems/xml-simple-1.0.11/
一千多行的文件,XmlSimple的作者可真能折腾啊。

Gem::Specification

要把项目打成gem包的话,需要在Rakefile.rb里加些东西:
spec = Gem::Specification.new do |s|
s.name="bvi_lib"
s.version='0.1'
s.summary = 'A Ruby libary for Batch Video Ingestion'
s.email = 'bdong@freewheel.tv'
s.require_path = 'lib'
s.autorequire = 'bvi_lib'

s.executables << 'bvi'
s.files = FileList["{lib,doc,bin}/**/**"].to_a
# s.has_rdoc = true
s.author = "Bin Dong"
# s.extra_rdoc_files = ["README"]
# s.rdoc_options = ["doc"]
s.add_dependency("activesupport", '~> 2.0.2')
s.add_dependency("actionmailer", '~> 2.0.2')
s.add_dependency("activeresource", '~> 2.0.2')
s.add_dependency("xml-simple", '~> 1.0.11')
s.add_dependency("fastercsv", '~> 1.2.3')
end
(提醒:是'xml-simple'和'fastercsv',而不是'xmlsimple'和'faster_csv'。前面是gem包的名字,后面是程序中require用的。其实两个弄成一样的多好啊。)

注意add_dependency方法,这个是添加包的依赖关系,第一个参数是依赖的gem包名,第二个参数是版本关系,具体见Programming Ruby Second Edition, P206, Table 17.1
这里只摘'~>'的说明:

“Boxed” version operator. Version must be greater than or equal to the specified version and less than the specified version after having its minor version number increased by one. This is to avoid API incompatibilities between minor version releases.

minor就是中间的那个版本号。如果版本号是2.01,则三个部分分别是major = 2, minor = 0, tiny = 1

要打gem包的话,直接在项目下运行:

rake gem

就可以了。

但是注意,只修改Rakefile.rb文件的话,是不会重新打包的,必须把原先的gem包删掉,再重新打包才行。
因为打包的时候会自动检查
FileList["{lib,doc,bin}/**/**"]
下的文件有没有被修改,如果修改了,则会重新打包,如果没修改,就不会重新打包。

Rakefile.rb里的内容,在打包的时候,是放在gem包的metadata里的,查看metadata,就能知道Rakefile.rb文件修改后,重新打包是否成功。

查gem的命令:

gem list
gem list|grep xml
gem list|grep csv

2008年6月2日星期一

cattr_accessor not found

I installed activeresouce using RubyGems. And I want to launch the tests:

ruby /var/lib/gems/1.8/gems/activeresource-2.0.2-/test/format_test.rb

But it didn't work, I've got an error:

./../../lib/active_resource/base.rb:150: undefined method `cattr_accessor' for ActiveResource::Base:Class (NoMethodError)
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'
from /usr/lib/ruby/1.8/rubygems/custom_require.rb:27:in `require'
from ./../../lib/active_resource.rb:38
from ./../abstract_unit.rb:4:in `require'
from ./../abstract_unit.rb:4
from load_test.rb:1:in `require'
from load_test.rb:1

So I googled it, and found this topic
http://www.ruby-forum.com/topic/59288
Before starting tests, run this command:

export RUBYOPT=-rubygems

And then:

ruby /var/lib/gems/1.8/gems/activeresource-2.0.2-/test/format_test.rb

Haha, it works, but I don't know how it works. :(

2008年5月29日星期四

Install Patch

When I tryed to install patch with the command:

sudo apt-get install patch

I have occurred:

Media change: please insert the disc labeled
'Ubuntu 7.10 _Gutsy Gibbon_ - Release i386 (20071016)'
in the drive '/cdrom/' and press enter


Resolve:
Comment out all the "deb cdrom:" lines in /etc/apt/sources.list

2008年5月26日星期一

One Foundation

人间百味,惟善最苦

经过这次,已经彻底不相信中国的官方慈善机构了。
以后捐款通通捐给壹基金,至少有德勒来审计,财务透明。
百度百科上壹基金的介绍:http://baike.baidu.com/view/944321.htm
官方网站:http://www.one-foundation.com/
壹基金的官网做得不错。第一次上中国红十字会的官网,我都以为是个假网站,太不专业了。

注意,壹基金不是独立的法人实体,而是挂靠在中国红十字会下的一个特殊组织,所以叫壹基金计划。壹基金有筹款权利,有监督权利,但是支出款需要红十字会批准。中国尚未批准公募的个人慈善基金。具体请看:http://finance.jrj.com.cn/news/2007-07-17/000002434244.html


"李连杰壹基金计划" 捐款方法
http://www.onefoundation.cn/html/cn/beneficence_01.htm

"李连杰壹基金计划" 是由中国红十字会博爱大使李连杰先生发起和倡导的一项为公益事业的筹款计划,他倡导每人每月最低捐出1元钱,帮助那些确实需要帮助的人们。基金的捐款全部进入中国红十字会总会帐户,并注明"李连杰壹基金计划"财务透明,定期公布。李连杰本人非常诚恳邀请社会与媒体共同监督。
捐款方式有以下三种:

1.使用财付通在线捐赠

2. 手机捐款 (999309 - 谐音是"救救救,善缘久")
如果您愿意捐1或2元,可以发送短信1或2到999309;如果您愿意每月捐款5元或10元或15元,可以发送短信MY5或MY10或MY15到999309。

3. 永久义工
如果您有能力并愿意成为"李连杰壹基金计划"的永久义工,您可以每年捐赠999元。
邮局汇款:
地址:中国北京东城区北新桥三条8号中国红十字会总会李连杰壹基金计划
邮编:100007
银行转账:
开户单位:中国红十字会总会 (请务必在附言处注明“李连杰壹基金计划”)
人民币开户行:中国工商银行北京分行东四南支行。
人民币账号:0200001009014413252
外币开户单位(account name):Red Cross Society of China Jet Li One Foundation Project
外币开户行(bank address):China CITIC Bank Beijing Jiuxianqiao Sub-branch
外币账号 (account number):7112111482600000209
外币开户行 (Outside of Mainland China):
China CITIC Bank Beijing
Jiuxianqiao Sub-branch
Red Cross Society of China
Jet Li One Foundation Project
外币账号 (Account #): 7112111482600000209
该计划期待人人参与,为他人,为自己。捐一元钱,出一点力,献一份爱。赠人玫瑰手有余香,在帮助他人的时候,您一定会感受到更多的幸福和快乐。
行动吧!"李连杰壹基金计划"等待您的参加。
最后提醒您注意,我们目前只有以上三种捐款方式,您不要相信其他途径的筹款。

2008年5月18日星期日

2008_5_12_14_28

为在地震中丧生的同胞默哀
从本月起,每月存一百元到专门帐户,作为将来灾难发生后的预备捐款。

2008年5月11日星期日

RailsCasts 006 symbol to proc

返回所有project的name,传统写法:
projects = Project.find(:all)
projects.collect { |p| p.name }
使用rails提供的shortcut:
projects.collect(&:name)
刚开始可能会觉得有点weird,不过习惯了就感觉很方便,还可以使用链式操作:
projects.collect(&:name).collect(&:downcase)
而且不止collect方法可以这样用,所有需要跟block的方法都可以用:
projects.all?(&:valid?)
projects.any?(&:valid?)
projects.each(&:save!)
意思是对每个project执行其save!方法,&后面跟的就是要执行的方法名称的symbol。
看完这个episode,不得不叹一句,编程就像是在说话一样,太强大了。

另外,在回复中有读者问到:如果&后的方法有argument的话,还能这样用吗?
作者的回复:Nope, it only works on very simple method calls which don't take an argument. Anything more complicated and you will need to use the full block.

还有老兄问能不能这样写:
ActorNames = Actor.find(:all).collect(&:last_name + ' ' + &:first_name)
作者推荐在actor model中定义一个full_name方法,然后调用这个方法:
ActorNames = Actor.find(:all).collect(&:full_name)

RailsCasts 005 using with scope

接上回episode 004,如果我们要查top 20 tasks而非全部记录,该怎么做呢?
不妨直接告诉聪明的find_incomplete方法:
@tasks = Task.find_incomplete :limit => 20
然后在方法定义中使用with_scope:
def self.find_incomplete(options => {})
with_scope :find => options do
find_all_by_complete(false, :order => 'created_at DESC')
end
end
当然,如果不用with_scope,或者还想传order条件(上面order是写死了的),可以merge options:
def self.find_incomplete(options => {})
find_all_by_complete(false, (:order => 'created_at DESC').merge(options))
end
或者使用reverse_merge:
find_all_by_complete(false, options.reverse_merge(:order => 'created_at DESC'))

在关联查询里,也可以传options进去:
@tasks = @project.tasks.find_incomplete :limit => 20
这里的task查询在两个scope里,一个是@project限定的scope,第二个是:limit限定的scope

RailsCasts 004 move find into model

在controller里:
@tasks = Task.find_all_by_complete(false, :order => 'created_at DESC')
改成:
@tasks = Task.find_incomplete
然后在model里定义incomplete方法,这是一个class方法,前面要加self:
def self.find_incomplete
find_all_by_complete(false, :order => 'created_at DESC')
end
注意方法定义体里的find…方法前不用加self,因为当前的作用域就是model类本身。
到这里都看似很平常,下面神奇的就来了。在episode 003里介绍的关联查询也可以用这里定义的find_incomplete方法:
@project = Project.find(params[:id])
@tasks = @project.tasks.find_all_by_complete(false, :order => 'created_at DESC')
可以改成:
@tasks = @project.tasks.find_incomplete
也就是说,凡是Task的class方法,都可以在@project.tasks这样的关联查询中使用。

RailsCasts 003 find through association

关联关系:
Project has_many Tasks
Task belongs_to Project
传统做法:
@project = Project.find(params[:id])
@tasks = Task.find(:all, :conditions => ['project_id = ? AND complete = ?'], @project.id, false)
Rails做法:
@tasks = @project.tasks.find(:all, :conditions => ['complete = ?'], false)
episode 002介绍的动态方法进一步简化:
@tasks = @project.tasks.find_all_by_complete(false)

RailsCasts 002 dynamic find by methods

传统的查询方法:
@tasks = Task.find(:all, :conditions => ['complete = ?', false])
@tasks = Task.find(:first, :conditions => ['complete = ?', false], :order => 'created_at DESC')
动态查询方法:
@tasks = Taks.find_all_by_complete(false)
@tasks = Taks.find_by_complete(false, :order => 'created_at DESC')
注意第二个,直接把:order这个hash作为第二个参数传进去就可以了。

RailsCasts 001 caching with instance variable

这是RailsCasts系列的笔记。

用实例变量来缓存数据库查询结果,这样就不用每次都访问数据库了:
@current_user ||= User.find(session[:user_id])
但是,实例变量的作用域是一次请求,第二次请求还是会重新去查数据库,得到的结果来给@current_user赋值。

2008年5月10日星期六

Fxxking docx

需要打开一个word文档,但后缀居然是docx,一问才知是office2007的杰作,用以前的office版本居然打不开。
上网一搜,倒是有解决方案,下一个补丁:http://download.microsoft.com/download/6/9/E/69EA942D-4636-4350-A526-0BFD9771A12A/O2007Cnv.exe
天啊,一个补丁居然要26.7M,谷歌金山词霸的安装包才23.2M啊。
于是在等待补丁下载的漫长过程中,上来写了这篇抱怨M$的blog。

2008年5月5日星期一

MySQL dump 命令

MySQL导出sql语句的命令:

mysqldump -u root database_name > database_name.sql

2008年4月23日星期三

jQuery API Browser

http://dev.jquery.com/view/trunk/tools/api-browser/
http://remysharp.com/jquery-api/相比各有优点,可以结合起来看。
前者有代码高亮和树型结构分类,后者可看example动态示例。
学习jQuery有新任务了:go through all functions
把API都过一遍。

2008年4月21日星期一

Getting started with jQuery

jQuery是一个轻量级的JS库,使用很方便,功能很强大。

API速查

这是目前出的两本书:
jQuery in Action (download)
Learning jQuery (download)

some resource:
15 Days of jQuery
Getting started with Aptana and jQuery

从今天开始学习jQuery了,一定要把jQuery学好。

2008年4月19日星期六

Slash自传

Slash出自传了,书名就叫Slash
本来已经下了个PDF版的,不过是图片扫描的,看起来不爽,就删了。
节选翻译版,请点here

有些写得挺有意思的:
  • (GNR与Aerosmith一起去巡演,Axl总是迟迟不出现)Steven Tyler找到我,问:“你们主唱呢?”。自打我们第一次巡演之后,每次我们相遇他都用这句话和我打招呼。
  • “哼,看来你最近没少练琴啊?”Joe Perry就这么问Jeff,我站在一边感觉巨尴尬,那TM可是Jeff Beck呀!(呵呵,每个人都有自己的偶像)
  • 组建天鹅绒手枪之前我们尝试过和他(Sebastian Bach,Skid Row的主唱)合作当主唱,但不合适。那个组合的结果怎么说呢,听起来就像Skid Roses(穷街 + 枪花 = 穷花?)
  • November Rain中的弦乐伴奏,我们根本没有雇交响乐团,全是Axl的合声器(Axl太牛了)。
  • “我们住进了下榻的酒店,为滚石的演出做准备。第一场演出前的一早,我接到一电话说Axl不打算参加这几场演出。他的理由是我和Steven吸毒太深。。。我们是,但那根本不重要。我们是去给滚石暖场,滚石乐队啊,哥们儿!(哈哈,太搞笑了)

2008年4月15日星期二

Install scim

安装中文支持,装完后就有scim了:
System -> Administation -> Language Support -> check Chinese

不过还要把scim设置成默认启动的输入法:

sudo cp /etc/X11/xinit/xinput.d/default /etc/X11/xinit/xinput.d/default.bak
sudo cp /etc/X11/xinit/xinput.d/scim /etc/X11/xinit/xinput.d/default

restart X (Ctrl + Alt + Backspace)

解决光标不能跟随的问题:
System -> Preference -> SCIM Input Method Setup -> Panel -> GTK, uncheck 'Embedded lookup table'
System -> Preference -> SCIM Input Method Setup -> Frontend -> Global Setup, uncheck 'Embed Preedit String into client window'

Then restart scim:

pkill scim
scim -d

Install VMware-Station

http://xinzhi.org/ubuntu-linux-vmware-workstation-65-beta/
Thanks to Dong Bin

install XP on VMware workstation:
中间有一步,要选'I will install the operating system later'

finish以后,在Edit Virtual machine settings -> CD/DVD 里,'Use ISO image',选要安装的xp iso文件,power on,安装xp。

安装VMware tools: 装好xp后,VM -> install VMware tools

与Host(ubuntu)机共享文件:在Host机上建一个用于共享的文件夹。
Edit Virtual machime settings -> Options -> Shared Folders,选择'Always enabled',click 'Add',Host path选刚才新建的共享文件夹。
然后在Guest(xp)机上,工具 -> 映射网络驱动器,文件夹选刚才在Host机上共享的文件夹。

2008年4月14日星期一

Ubuntu字体设置及环境变量

http://wenq.org/index.cgi?Debian_WQY

cd /usr/share/X11/fonts/misc
sudo gunzip wenquanyi_*pcf.gz


为autotest设置环境变量的时候,居然把:$PATH给忘了,以前写过blog的啊,该打。

export PATH=/var/lib/gems/1.8/bin:$PATH (:$PATH, add the orgi PATH)

source .bashrc


查看环境变量:

env
echo $PATH

在Ubuntu上搭建ROR开发环境

又要在新的Ubuntu上搭建ROR开发环境了。以前都是照着别人写的wiki来做,结果一段时间不做,又忘了。这次简要记一些步骤,纯粹备忘。

  • 设置网络(公司网络环境需要,特殊)
    Network settings -> Connections -> Properties -> (set static IP address)
    Gateway address: 192.168.0.1

    sudo gedit /etc/resolv.conf

      add line 'nameserver 192.168.0.3'
  • checkout code and sql schema from svn

    sudo apt-get install subversion

    svn co code and schema(伪命令)
  • 初始化数据库

    sudo apt-get install mysql-server mysql-admin
    sudo apt-get install libmysql-ruby

    下面四条是公司导数据库用的:
    source schema/createscheme.sql
    source schema/fwmrm_feedback_test.sql
    source schema/fwmrm_rpt_test.sql
    source schema/db_test/createscheme_test.sql
  • 安装ruby相关

    sudo apt-get install ruby
    sudo apt-get install irb
    sudo apt-get install rubygems
    sudo apt-get install libmagick9-dev

    install RMagick
  • 安装jre和aptana

    sudo apt-get install sun-java6-jre
    sudo apt-get install sun-java6-jdk
    sudo update-alternatives --config java

    (change JVM to SUN JVM)
    download aptana and unzip it
    Add RadRails to Aptana Studio by following the Start Page instructions (Help > Aptana Studio Start Page...)
    install subclipse (Help -> Software Updates -> Find and Install... -> Search for new features to install )
  • 安装autotest,see here

    sudo gem install ZenTest
    gedit .bashrc

      add line 'export PATH=/var/lib/gems/1.8/bin:$PATH'

    source .bashrc

  • 以下是公司开发需要的
    cd to ..\vendor\gem

    sudo gem install json

    cd ..\config\
    cp database.yml.dist.linux database.yml (change the content as needed)
    change '/var/lib/mysql/mysql.sock' to '/var/run/mysqld/mysqld.sock'
    change 'freewheel_test' to 'fwmrm_oltp_test'
    change 'reporting_test: -> database:' to 'fwmrm_rpt'
    change 'feedback_test: -> database:' to 'fwmrm_feedback'
    cp maui_config.rb.dist maui_config.rb

    sudo gedit /etc/mysql/my.cnf

      add the two lines 'max_allowed_packet      = 16M
    max_sp_recursion_depth=255'
    under "[mysqld] -> language = /usr/share/mysql/english"
    add this line 'max_allowed_packet = 16M'
    under "[client]"

Install RMagick

  1. grab the ImageMagick.tar.gz:
    wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
  2. Unarchive it:
    tar xvzf ImageMagick.tar.gz
  3. install ImageMagick
    cd ImageMagick-6.3.8 "Or whichever the current version is, of course."
    ./configure
    make (wait~~~)
    sudo make install
  4. download rmagick-2.2.2.gem:
    wget http://files.rubyforge.mmmultiworks.com/rmagick/rmagick-2.2.2.gem
  5. install rmagick:
    sudo gem install rmagick-2.2.2.gem
  6. (see http://rmagick.rubyforge.org/install-faq.html, search "libMagickWand.so.1"):
    add this line "export LD_LIBRARY_PATH=/usr/local/lib" to ~/.bashrc
  7. test
    ruby -rubygems -e "require 'RMagick'; puts Magick::Long_version;"
    irb -rubygems -r RMagick

ref: http://zzhang.javaeye.com/blog/162994

2008年4月9日星期三

非常男女、非常心理

结不结婚无所谓,但是一定要谈个恋爱。

恋爱中要完成的心理任务:
  • 更了解自己。
  • 培养爱别人的能力。

如果你真的爱过一个人的话,你会希望他过得好,即使自己必须离开。
如果你动了伤害一个人的念头,那么你根本就没爱过这个人,你爱的只是被爱的感觉。

谈恋爱的过程就是在“捞好处”

男人的恋爱情绪需求:
  • 能力要能被肯定。
  • 才华要能被欣赏。
  • 努力要能被感激。

女人的恋爱情绪需求:
  • 时常被关怀。
  • 需要再三的被肯定。“你到底爱不爱我嘛!”
  • 想法要能被尊重。

分手难过的原因:
  • 以前我是个受某某某喜欢的人,现在分手了,我再也不知道我是谁了,自我形象受到很大冲击,自尊受到伤害。
  • 以前那么多人你就选中了我,说明我是很特别的,现在我不再是独一无二的了。
  • 原来的稳定感被打碎了,计划全都无法实现了。

感情的成功与否,要看感情的品质,要看那两项心理任务是否达成了。
牵手不代表成功,分手不代表失败。

爱情保鲜计划:
  • 每天三次,每次三分钟。
  • 全神贯注,不受外界打扰,如关掉手机。
  • 浓情蜜意,毛手毛脚(肢体语言的沟通)。
  • 欣赏,感激。

每个女人心里的小问号:“我有没有讨人喜欢?”
每个男人心里的小问号:“我有没有让人瞧得起?”

有沟一定通

沟通出问题的原因:很多时候,我们急着跟别人沟通,是把精力都花在解决问题上面,而忽略了别人的情绪。
沟通的真正对象,是对方的心情。沟通完了后,双方的情绪都应该变得更好。
沟通的过程:开始,爽——中间,懂——结束,更爽。

真正重要的不是说了些什么,而是怎么说(肢体语言,表情,语气语调)。

跟一般人沟通有两个很重要的情绪需求:要被尊重被重视,被关爱。

学会说之前先学会听。

听的三个层次:我在,我在听,我在用心听。

常常问自己,我如果是他,我会有怎么样的感觉。

暂时避免去评论对方所说的。

EQ沟通的基本功:
  • 模仿对方的情绪状态。
  • 模仿对方说话的速度。

不应该说的十句话:
  • 你在骗人。
  • 你真是白痴,连这个都搞不懂。
  • 我才不管你怎么想呢。
  • 你根本就是胡言乱语嘛。
  • 跟你说那么多也没有用。
  • 你不喜欢也得照做。
  • 谁有时间听你说啊。
  • 我从来没见过你这么笨的人。
  • 你每次都这样。
  • 我懒得跟你啰嗦。

正确的做法:
  • 映:在沟通过程中,把自己当成镜子一样。把别人说的话,说话的内容,情绪状态都反映回去。
    • 别人:“我的男朋友跟我冷战了”。
    • 自己:“你的意思是说,你的男朋友都不理你了”。
  • 破唱片法:用跟对方不同的说话速度,重复播放(对方很大声,很激烈。而自己很小声,很缓和。直到对方的情绪也缓和下来)。
    • 小孩:“我要吃麦当劳,我就是要吃,我现在就要吃”。
    • 父母:“来,慢慢跟我说,为什么你要吃麦当劳”。

吵架三部曲:
  • 意见不和。
  • 态度之争(“你什么态度啊”)。
  • 面子之争。

在进入态度之争的时候,就要使用破唱片法。

接受批评:
  • 负责,承认:别人的批评有道理,就“闻过则喜”,勇于承认。
  • 模糊焦点:别人批评的不见得有理,但有不喜欢跟他吵。
    • 同意部分:同意别人讲得有理的那一部分。
    • 同意机率:同意这个状况会发生的可能性。“你说的也许对”。
    • 同意原则:把主词拿掉,换成一般性的。“你每次听到不中听的话,马上掉头就走,一点耐心都没有”。“的确,如果有人说着说着掉头就走的话,感觉很没有耐心”。
  • 推敲情绪:不去回答他表面的问题,而是回答他内心的情绪。

询问式:
  • 我想听听你有什么想法。
  • 我这样做会不会带给你什么困扰。
  • 那你希望我怎么做呢?
体谅式:
  • 我知道你很忙。
  • 我知道你不太同意这个做法。
提醒差异:
  • 我记得你上一次说……,你这一次又说……,你的真正意思到底是什么啊?
  • 不说但是:no but
  • 如果怎么样,会更好。

说服别人:
  • 用选择题代替是非题。
    “你要不要薯条?”
    “你的薯条是要大的还是中的?”
  • 加上“为了”
    “为了不耽误你明天的工作,请你赶快把这个东西做好交给我。”
称赞对方:从对方的外显行为来称赞他的内在。
  “你的报告写得很棒,看得出来你是个很有能力很专业的人”。

超级EQ超级销售

销售的定义:想办法去影响别人,达成共识的过程。

普通销售卖的是产品,EQ销售卖的是情绪。

一个产品有工具价值(使用价值),有情绪价值。

普通销售想的是如何赚别人的钱,EQ销售想的是如何服务别人的心。

两种重要的情绪:尊重,信任。

EQ销售前的准备:
  • 充实内在力量:传达热情,建立对产品的信仰,建立对工作的信仰(要喜欢所从事的工作,觉得工作有意义),不要害怕拒绝(别人说不,真正意思是“你还没有告诉我,为什么我值得花时间跟你聊一聊”)。

我们喜欢跟我们类似的人。
怎样让别人喜欢我:想办法让自己跟对方越接近越好。模仿对方的行为,配合对方的说话速度,了解对方的沟通习性。

人在沟通时,有三个不同的类型:
  • 视觉型,说话速度快,呼吸急促。“我看不出来这个事对我有什么好处”。
  • 听觉型,说话速度中等,呼吸速度中等。“听起来不错啊”。
  • 触觉型(感觉型),说话速度慢,呼吸慢。“我老是抓不住感觉”。

不要去假设别人需要什么,而是去问出对方的情绪需求。
从他过去使用的产品中,找出他的需求点来。

十大常见消费情绪:
快乐,安心,健康,关爱与浪漫,荣誉与骄傲,舒适,自由,接纳,有价值的感觉(物超所值),公平

EQ谈判的心理建设:
  • EQ谈判的对手是同志,不是敌人。
  • EQ谈判的对象是情绪,不是道理。
  • (谈完之后)双方皆high。

一开始的时候,提出较多的要求,以预留让步的空间。
避免造成对抗性的谈判。说“我们”而不要说“我”。
把对方的愤怒要视为是谈判的技巧,而不是一个情绪反应。
要让对方觉得他是胜利的一方,要让他觉得是赚到了:让步的空间要越来越小,速度要越来越慢。要让对方做最后的决定。
不要威胁对方他没有退路,而要告诉他我有退路。
当对方说不时,找出其真正的情绪需求。如对方说没有钱,其情绪需求可能是安心(因为没有钱,所以无法对未来安心)。如对方说没有空,其情绪需求可能是健康、自由、接纳。

人的两个相度:理性/感性,决断/深思

四种不同的人:
  • 务实型(理性,决断):个性严谨,做事一丝不苟,组织力很强,穿着正式,不喜欢不实在的包装,自信,独立,生活节奏快,喜欢接受挑战。
    • 情绪需求:希望做事有效率,需要别人的掌声,需要知道事实的真相。
    • 谈判技巧:跟他说话时速度要快,要清楚,言简意赅,要尊重对方的判断,强调自己已经被对方逼到了墙角。

  • 热情型(感性,决断):友善,喜欢寒暄,跟着感觉走。
    • 情绪需求:需要有感觉、需要被感动,需要受人喜欢(会委婉地说不),希望事情有效率。
    • 谈判技巧:跟他说话时速度要快,要带出热情,要动之以情,最好跟他问“你以前用什么样的产品”等等(跟务实性的人不要这样问)。

  • 和蔼型(感性,深思):有耐心,仁慈,体贴,办事速度会比较慢,重视人际互动,不喜欢说“不”,也不喜欢说“是”,花很多时间在思考,很重感情,不喜欢被迫做决定,不喜欢冒险。
    • 情绪需求:需要能够信任对方,希望被对方喜欢,不喜欢被逼到墙角。
    • 谈判技巧:建立信任关系,不要强迫他做任何决定,可以用一个问题来回答他的一个问题,比如:买家问:“小姐,有黑色的鞋子吗?”,卖家答:“你想要买黑色的鞋子吗?”(这招对别的类型的人不见得管用)。

  • 分析型(理性,深思):观察缜密,逻辑思考很强,对数字和时间非常敏锐精准,有完美主义倾向,做决定时凭证据说话,不会冲动
    • 情绪需求:买证据,买原则。
    • 谈判技巧:说话速度要慢,要提供详细的、按部就班的步骤来介绍,要强调佩服他的一丝不苟的精神,有事没事寄一些产品咨询给他。

积极乐观,面对挑战

这次要发的四篇文章,是张怡筠的有声书《EQ全方位成功系列》的摘抄。因为比较简练,单独看这个可能会看不太明白,最好结合听有声书。
--------------------------------------------------------------------------------------

摔跤不一定是坏事,关键是摔了后要赶快爬起来。

如果遇到挫败就一蹶不振,那么这次挫败很可能就会变成你一生中最大的失败,因为你再也爬不起来了。

挫折症候群:
  1. 有负面情绪,如:生气、沮丧、焦虑不安。
  2. 否定自我价值,觉得自己一无是处。
  3. 对未来产生绝望的感觉。

怎样处理生气的情绪:
  • 情绪的主宰是自己(错误的观念:我有权力生气)。
  • 身体是不“讲理”的,而是“讲情”的(无论自己有没有理,生气时身体的变化都是一样的。即使自己有理,一生气也同样会伤身)。
  • 愤怒就是利用别人的过错来惩罚自己(愤怒就是利用别人的愚蠢来惩罚自己)。
  • 要处理愤怒,重要的两个词:放下、原谅。
  • 怎么样原谅别人:写信法。
    • 发泄。把自己对他人的想法原原本本地写下来,要写真实的想法。
    • 原谅。站在对方的角度考虑问题,他冒犯我的原因是什么,帮他想想原因。写完了这些,再写“但是我决定原谅你,因为原谅你才能饶了自己”。

怎样处理沮丧(忧郁)的情绪:

小忧郁是心理上的感冒。

该怎么去检查是否有忧郁症:
  • 过去两个星期情绪持续低迷,对什么事情都提不起精神来。
  • 连续两个星期睡眠、食欲不正常。
  • 原来有兴趣的人事物,现在都没有兴趣了。

快乐心相法:
  • 心情不好的时候,先暂停生活的影片,自己在心里面大喊:“停——!”(把自己想象成导演,生活演得不好的时候,就喊停)。
  • 闭上眼睛,把注意力焦点集中在心脏,深呼吸,停十秒,让情绪停在那里。
  • 在大脑里幻想一些景象。幻想生活中很愉快、很兴奋、很温暖的经验。
  • 一次只会有一种情绪出现,所以用积极的情绪去替换消极的情绪。
  • 用心去思考,重新客观的看原来的问题。

挫折症候群的情绪长时间不处理的话,就会累积成压力。
压力就是长时间的身心失去平衡的状态。

真正决定我们压力感觉的,是我们怎么去定义这个事件。

面对重大改变时,抗变五部曲:
  1. 否定(不可能吧)。
  2. 愤怒(怎么会是我,为什么不是别人)。
  3. 讨价还价(如果事情还有转还的空间,我就怎么怎么样)。
  4. 沮丧(好像不幸真的发生了)。
  5. 接受(接受事实)。

打心理地基:
  1. 要乐于接受无常。
  2. 保持生活的弹性(不要太有原则)。
  3. 喜欢改变,拥抱变化。

积极乐观的做法:
  1. 为什么想法非常重要,态度在大脑里已经形成回路了,形成习惯了。
  2. 先看优点,再看缺点。
  3. 有自我掌控的能力(掌控自己的心情),不要把情绪浪费在自己无法控制的事情上。
  4. 承担责任。
  5. 控制问题范围。
  6. 生活中没有危机,只有状况。

悲观的因素:
  1. 全面化,以点概面。一件事情不好就认为所有的事都不好。
  2. 永久化,一辈子都会这样,没有终止。
  3. 个人化,认为都是自己的错。

Some books about JS

jQuery

Pro JavaScript Techniques

ppk on JavaScript

纯备忘,上面是传说中比较有名的JS方面的书,不知道什么时候才有空看。

2008年4月8日星期二

Aptana字体

Aptana的字体选择不正确的话,空格的宽度会窄于字符的宽度,也就是说两个空格的宽度看起来和一个字符的宽度差不多,这给代码缩进带来了很大的麻烦。

解决办法:
Window -> Preferences -> General -> Appearance -> Colors and Fonts -> Basic -> Text Font -> Change
选择 BitStream Vera Sans Mono (其他几个BitStream的字体应该也可以)

注:本Blog中,凡是Aptana没有特别指明的,一般都指RadRails

同时落水

在网上经常看到这个很欠扁的问题:“我和你妈妈同时落水,你先救哪个?”
回答自己不会游泳不算是最佳答案。
而应该反问:“你和你妈妈同时落水,你希望我先救哪个?”

自己去分析这一反问的奥妙。

基督山伯爵

  • 假如你想发现那做坏事的人,第一就先得去发现谁能从那件坏事中取利。
  • 上帝限制了人的力量,但却给他以无穷欲望。
  • 世界是一个客厅,我们必须客客气气地退出。
  • 凡是思想清楚的头脑,晚上临睡前的最后一个念头和早上醒来时的第一个念头便是最主要的念头。

(很久没有更新Blog了,一是懒,二是前段时间事情不少心情不好,其实还是写了些,不过没有贴上来罢了。写的大部分不是计算机方面的技术。我想本Blog的定位不仅仅是程序技术吧,所有的广义上的技术都可以,只要是理性分析的。慢慢的我会整理好了贴上来。本篇是前段时间看的小说,摘抄了几句,摘抄的也应该是理性的,讲道理或者讲真理的)

MySql查看版本和source命令

MySql下查看版本 :
从命令行进入MySql后,敲入\s,回车,即可。

MySql的source命令 :
都忘了是怎么用source命令了。直接在命令行下敲,不行。原来要先mysql -u root,进入mysql的命令行,再使用source命令。

这两篇写得很简单,也没有技术含量,纯粹是备忘。

InstantRails使用相关

Skype using port 80 :
在打开InstantRails时,提示80端口被skype占用了。这个问题以前的同事遇到过,所以不感到奇怪。
解决方法,在skype里,工具->选项->高级->连接,uncheck the one called “将80端口与443端口作为接入连接的备用端口”。

MySql env path :
使用InstantRails时,如果要在命令行下访问MySql的话,需要将其bin目录(如:E:\InstantRails\mysql\bin)加入系统环境变量中。
相应的,还应该把ruby的bin目录(如:E:\InstantRails\ruby\bin)也加入到环境变量中。

InstantRails folder name :
包含InstantRails的文件夹名称不能有空格。

2008年1月23日星期三

数据迁移命令

rake db:migrate

建立development的数据库结构

rake db:test:prepare

将development的数据库结构导入到test数据库中

rake db:fixtures:load

将test/fixtures中的数据读到数据库中(development数据库?)

rake spec:db:fixtures:load

将spec/fixtures中的数据读到数据库中(development数据库?)

Rails Environments and Configuration

Whenever you start a process to handle requests with Rails (such as a Webrick server), one of the first things that happens is that config/environment.rb is loaded. For instance, take a look at the top of public/dispatch.rb:
require File.dirname(__FILE__) + “/../config/environment”

A word of caution: If you were to set the RAILS_ENV environment variable to production here, or the constant variable RAILS_ENV for that matter, it would cause everything you did in Rails to run in production mode. For instance, your test suite would not work correctly, since test_helper.rb sets RAILS_ENV to test right before loading the environment.

因为在test/test_helper.rb中,先是:
ENV["RAILS_ENV"] = "test"
再是:
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
而在加载environment.rb时,又将RAILS_ENV覆写为prodoction。

项目中使用的Rails的版本:
# Specifies gem version of Rails to use when vendor/rails is not present
RAILS_GEM_VERSION = '2.0.1' unless defined? RAILS_GEM_VERSION

定义项目根目录:
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)

RAILS_ROOT is used all over the place in the Rails codebase for finding files


read the config/environment.rb file as text (minus the commented lines) and parse out the RAILS_GEM_VERSION setting with a regexp.


parse_gem_version(read_environment_rb)

def parse_gem_version(text)
$1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*'([!~<>=]*\s*[\d.]+)'/
end

def read_environment_rb
File.read("#{RAILS_ROOT}/config/environment.rb")
end

Default Load Paths:

def default_frameworks
[ :active_record, :action_controller, :action_view, :action_mailer, :active_resource ]
end

def default_load_paths
paths = ["#{root_path}/test/mocks/#{environment}"]

# Add the app's controller directory
paths.concat(Dir["#{root_path}/app/controllers/"])

# Then components subdirectories.
paths.concat(Dir["#{root_path}/components/[_a-z]*"])

# Followed by the standard includes.
paths.concat %w(
app
app/models
app/controllers
app/helpers
app/services
components
config
lib
vendor
).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) }

paths.concat builtin_directories
end

def builtin_directories
# Include builtins only in the development environment.
(environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : []
end

def default_plugin_paths
["#{root_path}/vendor/plugins"]
end

上面这个builtin_directories指的是:

It is the place for Rails to include application behavior (meaning models, helpers, and controllers). You can think about it as kind of like a framework-provided plugin mechanism.


http://localhost:3000/rails/info/properties
和在命令行下用script/about是一样的

在Rails项目里,绝大多数情况下,都不需要手动去load一个class或module,Rails有默认的规则去自动load需要的文件,其规则如下:
• If the class or module is not nested, insert an underscore between the constant’s names and require a file of this name. For example:
EstimationCalculator becomes require ‘estimation_calculator’
• If the class or module is nested, Rails inserts an underscore between each of the containing modules and requires a file in the corresponding set of subdirectories.For example:
MacGyver::SwissArmyKnife becomes require ‘mac_gyver/swiss_army_knife’

you should rarely need to explicitly load Ruby code in your Rails applications (using require) if you follow the naming conventions.

哈哈,顺我者昌

Rails::Initializer.run do |config|
...
end
里面的代码是关于Configuration的

# Settings in config/environments/* take precedence over those specified here.

The comment reminds you that the settings in the mode-specific environment files will take precedence over settings in environment.rb, which is essentially because they are loaded afterward and will overwrite your settings.


# Skip frameworks you're not going to use (only works if using vendor/rails).
# To use Rails without a database, you must remove the Active Record framework
# config.frameworks -= [ :active_record, :active_resource, :action_mailer ]
如果项目中不用数据库,就去掉Active Record,如果不用Web service或Email,也去掉相应的模块
为什么要去掉呢:

Ruby is, of course, interpreted, and if you can get away with a smallersized codebase for the interpreter to parse, you should do so, simply for performance reasons.


# Only load the plugins named here, in the order given. By default, all plugins
# in vendor/plugins are loaded in alphabetical order.
# :all can be used as a placeholder for all plugins not explicitly named
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
plugins默认是按照字母顺序load的,也可以指定先load哪些plugin,再load剩下的(:all)

Rails takes advantage of the fact that Ruby provides a callback mechanism for missing constants. When Rails encounters an undefined constant in the code, it uses a classloader routine based on file-naming conventions to find and require the needed Ruby script.

利用Ruby提供的"missing constants"回调机制,Rails根据命名惯例去找对应的文件来load。

Want to see the contents of your project’s load path? Just fire up the console and type $:

想看项目里的load path的话,就script/console,然后敲$:(注意加上后面的冒号),然后load path就打出来了,一长串。$:是一个数组。

除了Rails提供的三种environment外,还可以自定义environment,比如:

Use the normal environment settings for development mode, but point its database connection to a production database server. It’s a potentially life-saving combination when you need to quickly diagnose issues in production.

可以用来快速诊断生产环境的问题。

使用log的方法:在irb里敲入:

require ‘logger’

logger = Logger.new STDOUT

logger.warn “do not want!!!”


log的级别(按严重程度由低到高):
debug -> info -> warn -> error -> fatal
debug是开发时方便调试程序的,生产环境中用不到。
info是一些不常发生的事件,但仍然属于正常行为范围内。
warn表示正常范畴之外的事件发生了,需要注意,值得去研究研究问题,例如:
def create
begin
@group.add_member(current_user)
flash[:notice] = “Successfully joined #{@scene.display_name}”
rescue ActiveRecord::RecordInvalid
flash[:error] = “You are already a member of #{@group.name}”
logger.warn “A user tried to join a group twice. UI should not have allowed it.”
end

redirect_to :back
end
error表示还不需要重启服务器的错误。
fatal是指能想到的最坏的情况已经发生了,你的应用现在已经挂了,需要重启服务器了。

清空log/目录下的所有.log文件

rake log:clear


development.log里包含的信息:

• The controller and action that were invoked
• The remote IP address of the computer making the request
• A timestamp indicating when the request happened
• The session ID associated with the request
• The hash of parameters associated with the request
• Database request information including the time and the SQL statement executed
• Query cache hit info including time and the SQL statement triggering results from the cache instead of a roundtrip to the database
• Rendering information for each template involved in rendering the view output and time consumed by each
• Total time used in completing the request with corresponding request-per-second figures
• Analysis of the time spent in database operations versus rendering
• The HTTP status code and URL of the response sent back to the client


资源/链接:
Rails Plugins: Extending Rails Beyond the Core
http://weblog.jamisbuck.org/2007/1/31/more-on-watchingactiverecord
http://seattlerb.rubyforge.org/SyslogLogger/

Tabnav

项目里用到了个plugin,叫Tabnav,具体见:http://www.seesaw.it/en/toolbox/widgets/

tab的name和link需要自己写个类来定义,以前的开发者将它放在models目录下(不知道可不可以放在别的目录下),命名为xyy_tabnav.rb
class XyyTabnav < Tabnav::Base
add_tab do
named 'tab_1'
links_to(lambda{ { :controller => 'xxx', :action => 'show', :id => 1 }})
end

add_tab do
named 'tab_2'
links_to(lambda{ { :controller => 'yyy', :action => 'index' }})
end
end
在Tabnav这个plugin的目录下,有个generators文件夹,里面相当于是个demo,模仿着用就行了。

PATH设置

重装了Ubuntu,用gem装了ZenTest,然而在命令行下敲:

autotest

反应却是:

bash: autotest: command not found

然后去找了找gem的安装目录,一找吓了一大跳,原来的/usr/lib下面根本找不到gem的文件夹,于是用locate查了一下,发现在/var/lib下。
于是查看PATH:

echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games

没有包括/var/lib/gems/1.8/bin。

于是打开~/.bashrc,添加了一行:
export PATH="$PATH:/var/lib/gems/1.8/bin"
然后再:

source .bashrc


搞定,在项目下敲:

autotest

熟悉的自动测试又跑起来了。

CSS文字对齐

水平对齐:text-align
可用属性值:inherit, left, right, center, justify

垂直对齐:vertical-align
可用属性值:top, middle, bottom等

Haml中的注释

以前一直不知道怎么在Haml中注释掉一段代码。
结果官方文档上写得清清楚楚:
/

The forward slash character, when placed at the beginning of a line, wraps all text after it in an HTML comment. For example:

%peanutbutterjelly
/ This is the peanutbutterjelly element
I like sandwiches!

is compiled to:

<peanutbutterjelly>
<!-- This is the peanutbutterjelly element -->
I like sandwiches!
</peanutbutterjelly>

The forward slash can also wrap indented sections of code. For example:

/
%p This doesn't render...
%div
%h1 Because it's commented out!

is compiled to:

<!--
<p>This doesn't render...</p>
<div>
<h1>Because it's commented out!</h1>
</div>
-->
还是跟缩进相关。

至于反斜杠\(backslash),当然是转义字符啦。
%title
= @title
\- MySite

is compiled to:

<title>
MyPage
- MySite
</title>

具体请参考官方文档:
The full Haml reference
The full Sass reference
The RDoc documentation

CSS定位

假定元素A有绝对定位(position: absolute),其参照物规则(即它相对于什么元素进行定位):
  • 如果A没有被包含在任何其他 position: absolute || relative || fixed 的标签里,则A是相对于浏览器窗口进行定位的。
  • 如果A被包含在了 position: absolute || relative || fixed 的标签(假设标签名为B)里,则A是相对于B的边沿来进行定位的。

常常用relative定位来给嵌套标签建立一个新的定位上下文。即外层标签是relative定位,内层被包含的标签是absolute定位。
relative的意思可以理解为“相对于我”,即“我内部的所有定位元素,都应该以我为参照物来定位”。

使用z-index可以产生元素堆叠的效果,即某些元素覆盖在另一些元素上方。
网页默认的z-index是0,数字越大,就越堆在上方,即z-index:3的元素会盖住z-index:1的元素。

假设元素D包含了两个元素A和B,D的z-index为100,A的z-index为2,B的z-index为3。堆叠的效果是:A和B都堆在D的上方,而B又堆在A的上方。
D元素变成了它内部元素堆叠的起点,A和B的z-index值都是相对于D来说的,而D在其内部的z-index默认值为0,所以A和B都能盖住D。

z-index的值最好有一些间隙。10、20、30与1、2、3的效果一样,但是前者可以在原有堆叠序列的基础上,更方便地插入其他元素。
如果想要永远置顶一个元素(即永远使它显示在最上方,盖住其他所有元素),就给它一个很大的z-index值,如10000。

隐藏元素,display:none是不占据空间的,而visibility:hidden虽然隐藏了,但仍然占据空间。
如果把visibility:hidden应用到一个绝对定位(position:absolute)的元素上,则效果和display:none一样,不占据空间。因为使用了绝对定位的元素,其本身就已经从正常页面流上消失了,本身即使显示出来,也不占据空间,而是堆叠到其他元素上方。

与绝对定位不同,固定定位(position:fixed)始终相对于浏览器窗口而言。

时间管理(转)

李笑来写的系列文章《时间管理》:http://www.xiaolai.net/

专注是一种能够通过练习提高的能力。

从练习关闭某一个感官的知觉开始。比如,盯着一幅画,尝试着不去感受其他所有感官带来的刺激。关闭你的触觉、听觉、嗅觉甚至味觉。坚持两三分钟,如果你意识到你不小心体会到了视觉之外的感官刺激,那就重新开始。总计做上十五分钟。每天做一次,持续一个星期。

随后你要做的是,每个星期换一个感官去关注。比如,下个星期,你练习这个:闭上你的眼睛听音乐,然后开始尝试关闭你的视觉、触觉、嗅觉、和味觉。然后再下个星期再换一个,比如专注嗅觉而关闭其他的感官。


我想我不会去专门花时间练习,不过可以在做某些事的时候顺带练习。比如看书的时候,尝试关闭别的感官,只专注到视觉感官上。听英语或音乐的时候,只专注到听觉感官上。

时间分割法

挑出一件你认为最重要的事儿,然后,给自己做个时间表,在未来的一个星期乃至一个月的时间里,每天至少专注与这件事儿2个小时。

你可以参照所谓的时间分割法。比如,你需要在这件事儿上专注2个小时,即120分钟。那你应该把当天的任务分解成6块,而每一块用20分钟完成。你把20分钟当作你专注的基本时间单位,而每个时间单位过后,休息5分钟,想办法犒劳一下自己——喝杯咖啡或者牛奶。在属于休息时间的5分钟之内的最后1分钟,重新振作,尝试着恢复状态之后,进入下一个基本时间单位——另一个20分钟。

在你规划你的时间的时候,你应该明白为了能够完全专注120分钟,你最终需要规划出差不多150分钟左右的时间开销。


这个方法看来相当实用。

不要找借口“晚了”而逃避学习

如果,一个人不是很懒惰的话,那什么时候开始学都不晚啊!知道自己需要的是什么,真正的学习才开始。


定短期计划

如果一个计划的期限只有一个星期的时候,我是很容易坚持,并且往往可以出色地完成。

随着时间的推移,我竟然可以慢慢把期限延长,两个星期,一个月,甚至竟然可以指定一个季度的计划了!


早起的好处

晚上通常比较安静,做事基本不受打搅。可是,问题在于,早上的时间也通常不会受到干扰啊?


多想一步者赢

假设你要讲解的事情是A,那么你应该这么做:
1.尝试着把A全部搞清楚;
2.尝试着把A用清晰的逻辑讲解清楚;
3.在自己的脑子里想象一下听众会有怎样的反应(疑点,难点,迷惑之处),尽量去穷尽各种情况;
4.对听众重要的反应做出合理的回应;
5.把这些合理的回应综合到刚才第二步整理出来的内容中;
6.反复这个过程。


给别人做培训、讲课,就应该这样去准备。

So goes the world

so goes the world
意译:这就是人生
音译:狗日的世界

哈哈,开个玩笑。

2008年1月17日星期四

没有硝烟的战争

今天看到两条新闻,都是关于收购的。
一是MySQL被SUN收购了,二是BEA被Oracle收购了。
唯一的感觉就是震撼,特别是MySQL这件事上,不知其后命运如何,挺喜欢用MySQL的。一直传闻今年要IPO,怎么突然之间就被收购了呢?

一个公司能否做大做强有时候并不是单纯靠技术能力。技术很强,做得很好,就难逃被财大气粗的巨头收购的命运。
收购,真的是场没有硝烟的战争!

2008年1月16日星期三

项目中的selenium测试

the command is here http://svn.openqa.org/fisheye/browse/~raw,r=1000/selenium-on-rails/selenium-on-rails/doc/classes/SeleniumOnRails/TestBuilderActions.html

selenium测试就是模拟用户的行为,在页面上点(click)、输入(type)等,判断期望的结果。

在/test/selenium下面写好测试,用script/server -e test启动服务器,用rake test:acceptance到浏览器中进行测试。

这篇文章有点不太好归类,因为没有专门的名为Test的Tag(已经有TDD和BDD了,不想再添了),所以只能归到Tips下。

不用render模板时,不使用布局的方法

# Renders the template for the action "short_goal" within the current controller,
# but without the current active layout
render :action => "short_goal", :layout => false

# Renders the template for the action "long_goal" within the current controller,
# but with a custom layout
render :action => "long_goal", :layout => "spectacular"


Rails API文档上的。

喘口气,加加油,继续走

没想到,才短短八天的时间,就已经贴了四十篇文章了。其中一部分是这期间才写的,一部分是以前写的。不过以前写的最多占三分之一。
以后应该不会这么高产(这么疯狂)了。但会一直坚持写下去,写技术Blog的好处,才刚刚深有体会,以前实在是太懒了。

Mysql Ruby String 大小写排序陷阱(转)

以前一位同事的总结。

mysql 中: F > a
Ruby 中: a > F

说明: Mysql在字符串排序中, 大小写不敏感。
要小心在测试排序的时候, 注意这点。 可能会引起排序的test失败。

Display Aptana(RadRails) start page

每次打开Aptana后,都会出现start page,每次都要手动关掉,很麻烦。可以设置使其启动时默认不打开。
具体设置如下:
Windows -> Perferences -> Aptana -> Start Page -> Never display after startup
另一种设置:
Windows -> Perferences -> General -> Startup and Shutdown, 取消Aptana Intro的选中

A List of CSS Sites(转)

http://del.icio.us/tag/css
http://csszengarden.com/

以前一位同事给的CSS资源,很丰富的参考资料。

How to compare ActiveRecord objects

在ruby里,有三种方法可以比较对象
  • == 比较是否是同一个对象,可以被覆写
  • equal? 比较是否是同一个对象,不能被覆写
  • eql? 比较对象是否有相同的值

在写测试的时候发现,如果两个ActiveRecord对象有相同的类型,又有相同的id,则用==来做比较的话,得出的结果是true,即使它们的值不相同。可见ActiveRecord覆写了==方法。
例:
a = Account.create(:name => "aaa")
b = Account.create(:name => "bbb")
而因为不知名的原因,在测试的时候a与b有相同的id,即
a.id #=> 0
b.id #=> 0
于是
a == b       #=> true    #因为a与b类型相同,id相等,ActiveRecord就认为它们是同一个对象
a.equal?(b) #=> false #因为不是同一个对象
a.eql?(b) #=> false #因为值不相等

Multiple IE

http://tredosoft.com/IE7_standalone
http://download.microsoft.com/download/3/8/8/38889DC1-848C-4BF2-8335-86C573AD86D9/IE7-WindowsXP-x86-enu.exe

http://tredosoft.com/Multiple_IE
http://tredosoft.com/files/multi-ie/multiple-ie-setup.exe

先安装IE7,再安装Multiple IE

利用Rails Freeze生成本地Rails Doc

rails dummy_app
cd dummy_app
rake rails:freeze:gems
$echo >vendor/rails/activesupport/README
rake doc:rails


在 doc/api 目录下面会生成HTML版本的Rails Doc, 拷贝出来以后删掉dummy_app即可.

上面操作中第三步的作用的锁定当前 Rails 版本, 并将其复制到 vendor 目录下, 之后系统安装那个版本的 Rails 对于这个 app 来说都是无用的, 这样可以有效的避免由于 Rails 升级带来的兼容性问题.
通过 script/about 可以查看 app 当前 Rails 的版本.

TextMate theme for RadRails

注意,导入有风险,请先备份:
File -> Export -> General -> Preferences,点Next,选好目录,点Finish

想让你的RadRails外观像TextMate一样好看吗?
http://drnicwilliams.com/2006/08/08/textmate-theme-for-radrails/

只是有一个问题,导入后会把以前自定义的快捷键全都覆盖掉,全都恢复为默认设置,需要重新自定义一些用惯了的快捷键。寒一个,还好我自定义的还不是很多。

第二个问题就是,导入后,haml页面会变得很难看。
一个折衷的解决方法是,将haml的plugindisable或者删掉:
Help -> Software Updates -> Manage Configuration,找到有Haml Editor字样的那一项,点击右边的Disable。

然后设置用Ruby Editor打开haml文件,这时haml就全是黑底白字了,没有任何语法高亮,具体设置如下:
  1. Windows -> Perferences -> General -> Editor -> File Associations,在上面的File types里单击选中*.haml文件,然后点下面那个Add...,选择Ruby Editor。然后选中刚刚添加的那个'Ruby Editor', 点右边的'Default'按钮,将刚才选中的editor设置为默认的editor。
  2. Windows -> Perferences -> General -> Content Types,选择右边的Ruby Source File,点下边的'Add'按钮,添加一个*.haml就行了。
  3. 重启RadRalis。

实在不喜欢的话,就恢复到以前的设置吧,把最开始备份的文件导入。

gem安装指定版本的包

sudo gem install ZenTest -v 3.6.1

Load All Fixtures

rake db:fixtures:load RAILS_ENV=test

Hash中去掉不需要的key

Ruby中,Hash本身的delete是很方便,但也有不足的地方:
一次只能删掉一个key,而且返回值是对应key的value,而不是hash,无法做这样的链式操作:hash.delete(:key_1).delete(:key_2)
delete本身是破坏性的方法,会改变hash的值,而有时候我不想改变,只想得到一个新的hash。

于是自己实现一个:
def remove_keys(hash, *keys)
new_hash = hash.dup
keys.each {|k| new_hash.delete(k)}
new_hash
end

后来去查了一下Rails源代码,发现已经有现成的方法了,在activesupport/lib/active_support/core_ext/hash/except.rb中
# Returns a new hash without the given keys.
def except(*keys)
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
reject { |key,| rejected.include?(key) }
end

# Replaces the hash without only the given keys.
def except!(*keys)
replace(except(*keys))
end

感觉写框架的就是不一样,考虑到了很多,不像我那个,自己写自己用,确保按照正确方式用就行了,异常使用方式就没去处理(其实我那个方法一般是不会有什么异常使用方式的,因为dup和delete方法在HashWithIndifferentAccess类中已经被重写了,DHH已经帮我预先处理了异常使用方式)。
这里为什么要先判断一下能否响应convert_key方法呢?这就涉及到上次讲的HashWithIndifferentAccess,convert_key源代码如下:
def convert_key(key)
key.kind_of?(Symbol) ? key.to_s : key
end
像params这种,里面的key存的全都是string,你指定删除symbol的key,如果程序实现直接去删的话,就会不起作用(没有去掉指定的key)
然而,我将源代码改为:
def except(*keys)
rejected = keys
reject { |key,| rejected.include?(key) }
end
运行测试,还是全都通过了,看来测试代码没有覆盖到这种情况。

找到测试代码:
def test_except
original = { :a => 'x', :b => 'y', :c => 10 }
expected = { :a => 'x', :b => 'y' }

# Should return a new hash with only the given keys.
assert_equal expected, original.except(:c)
assert_not_equal expected, original

# Should replace the hash with only the given keys.
assert_equal expected, original.except!(:c)
assert_equal expected, original
end

改为:
def test_except
original = { :a => 'x', :b => 'y', :c => 10 }
expected = { :a => 'x', :b => 'y' }

# Should return a new hash with only the given keys.
assert_equal expected, original.except(:c)
assert_not_equal expected, original

# Should replace the hash with only the given keys.
assert_equal expected, original.except!(:c)
assert_equal expected, original

original = HashWithIndifferentAccess.new(:a => 'x', :b => 'y', :c => 10)
expected = HashWithIndifferentAccess.new(expected)
assert_equal expected, original.except(:c)
end
哈哈,测试报错了,看来我们刚才添加的测试代码起作用了,TDD的第一步,即写一段测试,使测试failed,已经完成了。
然后要做的就是完成代码使测试通过。这个很简单,把刚才改的代码恢复就可以了。
恢复后,再运行测试,通过了。看来我们刚才加的测试覆盖到了这行代码了:
  rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)

让我们再多想一下,如果去掉上面那行代码,不考虑HashWithIndifferentAccess的情况,那么就下面这一行代码就实现了删除指定key的功能了。
def except(*keys)
reject { |key,| keys.include?(key) }
end
晕死,原来就这么简单呀,到处去找不改变hash本身的delete方法,其实reject就是:

Same as Hash#delete_if, but works on (and returns) a copy of the hsh. Equivalent to hsh.dup.delete_if.


最后查了一下项目,发现在Rails 1.2.5里面,except方法是在vendor/plugins/will_paginate/lib/will_paginate/core_ext.rb中定义的。
准确说是Rails 1.2.5里没有这个方法,而是由will_paginate这个分页的plugin提供的。Rails 2.0把它移到了activesupport里。
以后关于Rails源代码,没有说明具体版本,都默认为Rails 2.0