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. :(