苹果之夜

6月9日清晨, 打开Google Reader, 满世界的苹果新闻, 噢~ 原来昨晚是苹果之夜(WWDC2009).

我不是什么果粉, 我只是凭直觉选择自己需要的东西, 浏览一下看看都有什么:

  • iPhone 3GS
    • S代表速度(speed)
    • 摄像头强了
    • 罗盘是真的
    • 其次还有一大堆本来就该有的功能终于有了
    • 价格: 16G - 199美刀, 32G - 299美刀. 8G不带S - 99美刀.
  • Snow Leopard
    • 9月上市
    • 体积减小6G, PPC的末日!
    • 发布消息之后购买的Mac可以加88元升级系统.
  • MacBook
    • 13吋铝本纳入Pro家族了, MacBook家族只剩下小白了, 小白~ 你要挺住~.
    • 另添新丁15吋, 再加上原有的17吋, 目前全系列共有三种规格.
    • 提高了电池续航
    • 13吋增加了SD卡插槽…
    • Air家族价格集体缩水, 13吋的Air官方标价不到12K
  • Safari
    • 据说是蛮强的
    • 已经下载好了, 准备更新.
    • 这篇文章是在Sarafi3下面发布的最后一篇了
    • 升级

Adobe Flash Builder 4 Beta1 for Mac

六一那天放假应该不包括你吧, 那你一定不会错过 Adobe 发布 Flash Builder 4 beta1 的利好新闻.

对于 Adobe 的官网, 五年以来一直给我留下深刻印象, 访问和下载的速度因为实在是太慢了, 虽然这五年间我已经从 Windows 变成 Mac, 从网通变成电信, 速度仍然没有起色.

关于 Flash Builder 的介绍显得有点多余, 因为如果你既然了解这个名字, 就一定知道怎么去用, 若是和我一样面对着 Mac 版的下载链接无奈的话, 试试下面这个地址:

flashbuilder4_b1_mac_060109.dmg

十进制 IP 地址

“十进制网址”这个词汇, 从诞生之日起就一直摆脱不掉一双引号, 关于这项牛X技术的文章请自行搜索.

具体的实现过程是这样的:

今有IP一枚: 10.1.2.3

则”十进制”格式的计算方法是:

1
2
3
10*256^3 + 1*256^2 + 2*256^1 + 3*256^0
167772160 + 65535 + 512 + 3
167838211

逆向运算就是求商取余的过程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def decimal_ip_transform(ip)
powers_256 = (0..3).map { |p|
256**p
}.reverse.freeze

case ip
when Integer
powers_256.map { |p|
[ip/p, ip %= p].first.to_s
}.join(".")
when String
ip.scan(/\d{1,3}/).zip(powers_256).inject(0) { |acc,a|
acc + a.first.to_i*a.last
}
end
end

可是这有什么用呢? 申请科研经费?

Stop! 如果你这样想, 请你离开, 谢谢配合!

====================虚荣与务实的分隔线====================

务实派请往下看:

最近有个需求是用IP地址来区分一种终端设备的地理位置, 需要在数据库里记录IP地址对应的设备信息.

直接将”192.168.0.3”这样的字符串保存起来, 读取时不太容易匹配, 如果加长到”192.168.000.003”的话就能支持排序的过滤等操作, 可是体积也变大了了.

如何缩小这一列的宽度的同时实现需求呢? 上文提到的方法值得一试, 把字符串转化成整数保存, 范围在 0 到 4294967295 之间, 而且是线性增长的, 相邻的IP字符串转化的数字也是相邻的, 查找 BETWEEN 167772160 AND 167772160+255 肯定要比查找 LIKE “10.0.0.%” 快的多吧!

PS: 文中除了代码是真的, 其它全是假的, 例子也是假的, 只是为了记录突然冒出来的怪异想法, 关于文章题目请勿深究.

F# May 2009 CTP

VS2010 beta1已经可以下载, F# 内置其中, 项目模板仍然只有3个(程序, 库, 教程).

VS2008 插件同时发布.

或者直接下载源代码.

貌似这段时间 F#项目组的工作也只是与VS2010整合, “看的见”的变化应该是体现在数据可视化设计方便, 至于许多人关心的若干设计器F#版, 我认为没有很正常, 以后也不会有.

语法上的改变详见MSDN.

对于新的VS2010, 普遍反映速度慢了许多, WPF实现的界面, 果然不出所料.

由于现在开发环境不方便, 没法亲身体验安装, 引用Don的一张图.

Hide Unix Files in Mac OS X

对 bash 的极度不熟悉, 失手 rm 了 /tmp 目录.

理所应当的想 mkdir /tmp 就应该没事了吧?

昨天仔细看过才明白, 原来在 Mac 下面 /tmp 只是个替身(symbolic).

真实路径指向 /private/tmp

在 /private/ 下面还有我们熟悉的 etc, var 等等.

那么我也建立一个吧: ln /tmp /private/tmp

问题来了, 怎么把这个目录在 Finder 中隐藏起来呢?

一阵 chmod chown … 未果…

今天搜索了一下 apple 论坛, 发现这样一张贴子, 顺着这张贴子找到了这样一篇文章, 在文章的回复里看到那样一个办法.

其实很简单, 在根目录建立一个 .hidden 文件, 里边写上你不想显示的文件/目录/替身名称就好了, 例如:

1
2
sudo echo tmp >> /.hidden
sudo echo 用户手册和信息 >> /.hidden`

然后注销一下就搞定了.

问题的解决办法好像和问题本身并不是一回事, 但是我也没有搞明白为什么论坛里提到的使用 Develop Tools 里的 SetFile 为什么就没有效果. 暂且放下完美主义吧.

Leopard 上尝试 lightTPD + FASTCGI + Rails

拜读ribbonShiningRay的性能分析贴, 决定在 mac 上尝试一下 lightTPD + FastCGI 这种 rails 部署方式.

关于如何在 Leopard 上面编译安装, 这里有两篇极为详细的教程:

http://hivelogic.com/articles/view/ruby-rails-leopard

http://hivelogic.com/articles/view/installing-mysql-on-mac-os-x

同样也是从这个博主的文章中弄懂了 Unix-like 系统的目录结构, 明白了为什么要把自己编译的软件安装在/usr/local/下面. (03年就把自己闷在宿舍装 RedHat, 可是后来一直对 Unix-like 知之甚少.)

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

有了前辈的探索, 这一路装来倒也没碰上太大的麻烦, 不过还是要注意以下几点:

ruby1.9 和 ruby-fcgi 不合, 研究如何部署到生产环境的话, 还是不要贪玩的好.

编译 mysql 的时候记得加 max 参数, rails 好多操作都是靠事务支持的, 所以离不开 InnoDB.

--with-plugins=max-no-ndb

关于下面的安装过程:

curl -O 代表下载文件, 要是你不喜欢这种方式, 也可以用别的软件来下载.

sudo 的前提是你得有个密码, 否则在输入密码那里直接回车是不被接受的.


第一步: 配置 PATH

编辑 .profile

1
mate ~/.profile

根据具体情况加入如下内容:

冒号分隔, 想要先查找的路径放前面, 最后那个 $PATH 代表系统原有的路径, 我们把它放到最末尾.

现在我们还没有这么多软件, 只是提前准备着.

1
export PATH="/usr/local/ruby/bin:/usr/local/mysql/bin:/usr/local/lighttpd/bin:/usr/local/fcgi/bin:/usr/local/pcre/bin:/usr/local/bin:/usr/local/sbin:$PATH"

重新加载配置

1
. ~/.profile

OK~, 根据前文提到的博主的建议, 让我们给源码建立一个目录统一管理:

1
2
3
4
sudo mkdir -p /usr/local/src
sudo chgrp admin /usr/local/src
sudo chmod -R 775 /usr/local/src
cd /usr/local/src

第二步: 安装 Ruby

虽然已经内置 ruby1.8.6 了, 不过我只是想亲自试一下安装过程.

内存大补贴(MBARIpatch)地址有点乱, 自己下载吧.

http://sites.google.com/site/brentsrubypatches

1
2
3
4
5
6
7
8
9
curl -O ftp://ftp.ruby-lang.org/pub/ruby/1.8/ruby-1.8.7-p72.tar.gz
tar xzvf ruby-1.8.7-p72
tar xzvf MBARIp72patches.tar.gz
MBARIp72patches/apply ruby-1.8.7-p72
cd ruby-1.8.7-p72
./configure --typo:codefix=/usr/local/ruby --enable-shared --enable-pthread CFLAGS="-O2 -fno-stack-protector -D_XOPEN_SOURCE=1"
make
sudo make install
cd ..

检查一下安上了没有:

1
2
which ruby
ruby -v

第三步: 安装 RubyGems

在你的 Mac 上面会有很多预置的 gem, 而且还 cleanup 不掉, 要是实在觉得碍眼, 可以查看一下路径, 然后重命名屏蔽掉, 但是千万别删除内置的 ruby, 否则后果很严重 (例如: textmate 失明.)

1
2
3
4
5
6
7
gem environment

curl -O http://rubyforge.org/frs/download.php/45905/rubygems-1.3.1.tgz
tar xzvf rubygems-1.3.1.tgz
cd rubygems-1.3.1
sudo ruby setup.rb
cd ..

gem 命令可以通过以下命令查看

1
gem help commands

命令参数是可以进行简化输入的, 只要不产生歧义. 例如:

1
2
gem e   # gem environment
gem sea # gem search

第四步: 安装 Rails

网慢是么? 把 rails 连同依赖包都下载到本地安装吧. 查看一下 rails 都依赖哪些 gem

1
gem dependency rails -v 2.3.2 -r

写这些文字的时候我看到的结果是6个 (其中 rake 要求 >= 0.8.3, 不过实际安装时却管我要 0.8.4, 不晓得为什么)

  • rake (>= 0.8.3, runtime)
  • activesupport (= 2.3.2, runtime)
  • activerecord (= 2.3.2, runtime)
  • actionpack (= 2.3.2, runtime)
  • actionmailer (= 2.3.2, runtime)
  • activeresource (= 2.3.2, runtime)

http://rubyforge.org 把这些 .gem 文件都下载回来, 放在一起, 文件名不要改.

1
2
sudo cp /Downloads/*.gem .
gem install -l rails-2.3.2.gem

速度很快吧~ 要是不安装文档会更快.

1
gem install -l rails-2.3.2.gem --no-rdoc --no-ri

第五步: 安装 Mysql

网上流行的编译参数, 具体这些参数的含义, 我这个菜鸟目前还无法回答, 注意 —with-plugins=max-no-ndb

编译和安装的过程有点长 (加起来大约20分钟), CPU 有点热…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
curl -O http://mysql.mirror.tw/Downloads/MySQL-5.1/mysql-5.1.33.tar.gz
tar xzvf mysql-5.1.33.tar.gz
cd mysql-5.1.33
CC=gcc
CFLAGS="-O3 -fno-omit-frame-pointer"
CXX=gcc
CXXFLAGS="-O3 -fno-omit-frame-pointer -felide-constructors
-fno-exceptions -fno-rtti"

./configure --typo:codefix=/usr/local/mysql
--with-extra-charsets=complex --enable-thread-safe-client
--enable-local-infile --enable-shared --with-plugins=max-no-ndb
make
sudo make install
cd ..

既然是 DIY, 你就得自己运行脚本填充最初的系统表.

1
2
3
cd /usr/local/mysql
sudo ./bin/mysql_install_db --user=mysql
sudo chown -R mysql ./var

mac os x 是靠 launchd 守护进程运行的, 配置文件用的是 PropertyList (XML 格式), 通过观察发现每10秒检查一次, 发现进程不在就启动.

现在我们让 launchd 去看着 mysql, 保证它 7*24 都在运行.

方法是新建 /Library/LaunchDaemons/com.mysql.mysqld.plist 写入如下内容.

问:为什么是这个名字?
答:我看 apple 内置的文件都是这样命令的, 看起来像是 com.公司.产品.plist

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<true />
<key>Label</key>
<string>com.mysql.mysqld</string>
<key>Program</key>
<string>/usr/local/mysql/bin/mysqld_safe</string>
<key>RunAtLoad</key>
<true />
<key>UserName</key>
<string>mysql</string>
<key>WorkingDirectory</key>
<string>/usr/local/mysql</string>
</dict>
</plist>

加入守护清单

1
sudo launchctl load -w /Library/LaunchDaemons/com.mysql.mysqld.plist

查看清单

launchctl list

解除守护

1
sudo launchctl unload -w /Library/LaunchDaemons/com.mysql.mysqld.plist

有了自己编译的 mysql, 当然少不了那个 C 版本的 ruby 驱动.

1
2
3
4
5
6
7
curl -O http://rubyforge.org/fsr/download.php/51087/mysql-ruby-2.8.1.tar.gz
tar xzvf mysql-ruby-2.8.1.tar.gz
cd mysql-ruby-2.8.1
ruby extconf.rb --with-mysql-dir=/usr/local/mysql
make
sudo make instal
cd ..

mysql 的 make 文件比较完整, 你可以随时通过这份编译好的源码卸载掉它

1
sudo make uninstall

第六步: 安装 FastCGI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
curl -O http://www.fastcgi.com/dist/fcgi-2.4.0.tar.gz
tar xzvf fcgi-2.4.0.tar.gz
cd fcgi-2.4.0
./configure --typo:codefix=/usr/local/fcgi
make
sudo make install
cd ..

curl -O http://rubyforge.org/fsr/download.php/11368/ruby-fcgi-0.8.7.tar.gz
tar xzvf ruby-fcgi-0.8.7.tar.gz
cd ruby-fcgi-0.8.7
sudo ruby install.rb config -- --with-fcgi-include=/usr/local/fcgi/include --with-fcgi-lib=/usr/local/fcgi/lib
ruby install.rb setup
sudo ruby install.rb install
cd ..

第七步: 安装 lightTPD

ribbon 的文章里告诫我们要先装 PCRE, 我们不要去怀疑:

1
2
3
4
5
6
7
8
curl -O ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-7.8.tar.gz
tar xzvf pcre-7.8.tar.gz
cd pcre-7.8
CFLAGS='-O2 -Wall'
./configure --typo:codefix=/usr/local/pcre
make
sudo make instal
cd ..

有了前辈经验作为铺垫, 安装倒也简单:

1
2
3
4
5
6
7
curl -O http://www.lighttpd.net/download/lighttpd-1.4.22.tar.gz
tar xzvf lighttpd-1.4.22.tar.gz
cd lighttpd-1.4.22
./configure --typo:codefix=/usr/local/lighttpd --with-pcre=/usr/local/pcre
make
sudo make install
cd ..

把默认配置文件复制出来

1
cp ./doc/lighttpd.conf /etc/lighttpd.conf

至于怎么配置, 有点超出我的能力范围了, 我只能勉强让它跑起来. 我只改动了下面几处:

  • 开几个模块 mod_ rewrite access fastcgi simple_vhost cgi comtypo:codess accesslog
  • 指定路径的时候不能用 ~/ 这样的路径
  • 指定日志 accesslog.filename = “/var/log/lighttpd/access.log”, 这里的路径不是硬性规定, 但是记得如果没有这个目录的话要先创建.

lighttpd 配置:

$HTTP["host"] =~ ".+" {
    server.document-root = "/Users/colder/Sites/rails/public"
    server.error-handler-404 = "/dispatch.fcgi"
    fastcgi.server = (".fcgi" =>
        ( "localhost" =>
            ( "min-procs" => 3,
                "max-procs" => 3,
                "socket" => "/tmp/rails.socket",
                "bin-path" => "/Users/colder/Sites/rails/public/dispatch.fcgi",
                "bin-environment" => ("RAILS_ENV" => "development")
            )
        )
    )
}

launchd 的配置过程和 mysql 相似, 但也有点特殊. 从这份文件中可以看出如果守护的程序是带参数的, 那么第一个参数必须是程序本身. 参数名与参数值之前还不能留空格.
新建 /Library/LaunchDaemons/com.lighttpd.lighttpd.plist 写入如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>KeepAlive</key>
<true />
<key>Label</key>
<!-- 这就是个名字, 会显示在 launchctl list 里看到. -->
<string>lighttpd</string>
<key>OnDemand</key>
<false />
<key>Program</key>
<string>/usr/local/lighttpd/sbin/lighttpd</string>
<key>ProgramArguments</key>
<array>
<string>/usr/local/lighttpd/sbin/lighttpd</string>
<!-- 这行很奇怪是吧? -f 后面紧挨着参数值, 你要不这样写, 就甭想启动. -->
<string>-f/etc/lighttpd.conf</string>
<string>-D</string>
</array>
<key>RunAtLoad</key>
<true />
</dict>
</plist>

然后加载这个文件:

1
sudo launchctl load -w /Library/LaunchDaemons/com.lighttpd.lighttpd.plist

有了 launchd, mysql 和 lighttpd 的运行基本上不用担心了, 那 rails 的运行靠谁呢? 答案是 lighttpd. 配置文件说明了这一点, 当 lighttpd 启动的时候, 它会启动若干个 rails 实例 (数量由 min-procs 和 max-procs 决定). 即使是中途手动杀掉了 ruby 进程, 只要有请求进来, lighttpd 还是会去启动 rails 实例进行响应的.

OK~ 如果人品没有什么大问题的话, 到这里应该已经跑起来了, 这也正应了 rails 官方的那句台词:”先跑起来再说!”

如果跑不起来呢? 嗯! 我就碰到了下面的这个问题: 在 Windows 创建的 rails 项目, 复制到 Mac 上面就跑不起来了. 碰到这种情况, 看看你的rails目录下面的 public/dispatch.fcgi 开头第一句, 如果类似于: #!C:/Ruby/bin 这样的话, 请竖起中指!

然后心平气和的改成

1
#!/usr/local/ruby/bin/ruby

还有, 不要可惜了你的 Mac, 她不只有个光鲜的外表, 里边也很有内涵, 例如 tail

想知道 lighttpd 为什么启动不了的时候可以用它查看错误日志, 然后再一边修改配置, 一边回来查看这10秒一次的新日志.

1
tail -f /var/log/lighttpd/error.log

当然, 用在 rails 开发的时候会更合适:

1
tail -f ~/Sites/rails/log/development.log

经过这一番折腾, Rails 开发环境总算是跑起来了.

不是部署环境么?

呃. 就我目前的水平, 还不敢谈部署, 只想着用 ribbon 推荐的部署环境进行开发, 就已经满意了, 至于什么时候谈部署, 还得慢慢来, 现在水平还太烂, 深入学习 *nix 和 rails 才一个月, 不敢奢望太多.

至少目前很多问题还不好解决, 例如:

不知道为什么后端有时候会死掉, 而且 lighttpd 又不会去重开. (手动杀的就可以重开).

偶尔还会出现连接失败(开发环境, 小量请求).

好吧~ 世上没有十全十美, 不是么?

Rails Ev(i|a)l @ April Fools Days

First. Create a evil, oh no, a eval controller.

app/controllers/ruby_eval_controller.rb

1
2
3
4
5
6
7
8
9
Class RubyEvalController < ApplicationController
def do
if @result = eval(params[:ruby])
render :xml => @result
else
head :ok
end
end
end

Then. Let’s post some evil params.

POST /ruby_eval

1
2
<?xml version="1.0" encoding="UTF-8" ?>
<ruby>User.find_by_intro('I am eval or evil?')</ruby>

弄懂 *Nix 文件夹

一直以来对类 Unix 系统都没有太深入的研究过, 许多问题不求甚解.

例如这个关于文件系统目录结构的问题:

  • /bin 和 /sbin 的区别是什么?

  • /bin 和 /usr/bin 有什么区别?

  • /lib 和 /var/lib 又有什么区别?

更奇怪的是为什么 /usr/local 里面又有一套 bin lib?

今天无意间在 hivelogic 的博客里找到了答案:

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

以下标准不是硬性的, 也就是说每个变种都或多或少的有所违背, 但总体上还是保持尊重.

  • / 根目录, 文件体系的入口.

  • /home 用户自己的根目录.

  • /root root 帐户的 home.

  • /bin binary 单用户模式必需的可执行文件, 也就是所有用户都要用到的.

  • /sbin system binary 系统必需的可执行文件.

  • /lib library 一些库文件, 供 /bin 和 /sbin 使用.

  • /etc 存放系统级别的配置文件. (et cetera 其他, 等等… 貌似是法文)

  • /boot 启动和初始化所需的文件.

  • /dev device 设备. 在 *nix 的概念中, 设备也是文件.

  • /media 可移动媒介安置点, 例如光驱和U盘.

  • /mnt 文件系统临时安置点. (不清楚和上一个的区别)

  • /opt optional 可选的应用程序软件包.

  • /tmp temp 临时文件, 重启消失.

  • /usr user 文件体系的第二级, 存放多用户应用程序,

  • /usr/local 文件体系的第三级. 在这里编译安装软件对系统的影响最小.

  • /var variable 易变的文件. 日志, 打印, 邮件等.

其中 /bin /usr/bin 和 /usr/local/bin 实际上是代表了三个阶层, 第一阶层所有用户通用, 第二阶层可以针对每个用户不同, 第三个阶层是用户之间相对独立的.

Flex 提交 Xml 到 Rails

如何从 Flex3.2 调用 Rails2.3.2 的 Restful API?

所谓 Restful API, 简单的说就是利用已有的四个 HTTP 动作实现对资源的四种操作:

  • GET 获取
  • POST 创建
  • PUT 更新
  • DELETE 删除

在这里对 REST 不作更详尽的解释, 相关资料网上有很多, 争议更多. 我们暂且不管这些争论(包括作者自己也在争论), 单从纯技术角度来看, 这种方式对于我们常见的 CRUD 操作实在是再方便不过了.

但是且慢, 如果你拥有一个独立的客户端, 发送这四种动作是不成问题的, 可是如果你是做 RIA 开发(Flex 或 Silverlight), 目前的浏览器宿主只能支持 GET 和 POST. 后两种动作怎么办?

注: 虽然 Flex 里的 HTTPService 组件有8种动作, 但并不起作用.

也许你已经见过很多动态网站系统采用的解决办法了, 那就是加 URL 参数.

http://www.example.com/admin.asp?action=newpost&title=abc

这样做的好处是通过直观的参数与服务器进行对话.

当然坏处也不少, 按照 HTTP 协议, GET 动作是不能对服务器有副作用的, 因为 GET 只负责读取, 另外这样的 URL 如果被搜索引擎收录了, 或者被用户加入收藏夹, 虽然不是什么致命的问题, 但终究还是对 HTTP 协议的一种误读.

OK~, 既然浏览器不支持, 提倡 Restful 的框架又普遍不满意这种 URL 参数的工作方式, 那 rails 是如何解决这个问题的呢?

Rails 也没有能力解决浏览器的问题, 她同样需要使用额外的方法来模拟 PUT 和 DELETE 动作, 查看产生的源代码, 你会发现她采用的方法是读取 POST 表单里的 hidden 域 _method

1
<input name="_method" type="hidden" value="put" />

采用这种办法对于网页的表单来说或许是合适的, 但对于像我这样使用 Flex 开发 RIA 客户端程序的人来说, 会存在下面的问题:

我读取的文档可能是这样的

1
2
3
4
<user>
<name>路飞</name>
<age>20</age>
</user>

那么我为什么要转换成类似下面的表单来提交?

user[name]=路飞&user[age]=20

如果可以读取也是 xml, 提交亦是 xml, 生活就简单的多了, 不是么?

提交 xml 就意味着 _method 必须放在别的地方(xml 是不能有两个根元素的), rails 2.2.2 之前的版本(具体到哪一版并不清楚), 服务器是接受 GET 参数的, 也就是说可以写成这样:

/users/1?_method=PUT

_method 虽然有了安身之处, 但是且慢! 这样安全吗? 正规吗? 我想 rails 的开发者也对这个东西看不顺眼, 所以在 rails 2.3.2 里, 这种方法被禁止了.

详见:

lib/action_controller/vendor/rack-1.0/rack/methodoverride.rb

新版本只在 POST 表单里读取 _method, My God…

幸运的是, methodoverride.rb 这个文件的内容为我指明了另一个方向:

1
2
3
4
5
6
7
8
9
10
11
12
def call(env)
if env["REQUEST_METHOD"] == "POST"
req = Request.new(env)
method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
env[HTTP_METHOD_OVERRIDE_HEADER]
method = method.to_s.upcase
if HTTP_METHODS.include?(method)
env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
env["REQUEST_METHOD"] = method
end
end
end

这个文件告诉我们, 服务器除了接受 _method 隐藏域之外, 还接受一个叫 “HTTP_X_HTTP_METHOD_OVERRIDE” 的消息头, 太棒了, 让我们迎着光明继续前进~

为了能够看到提交的消息头, 我们需要在 application_controller.rb 里添加点代码. (Rails 2.2.2 之前该文件名为 application.rb)

1
2
3
4
5
6
class ApplicationController < ActionController::Base
before_filter :print_headers
def print_headers
request.headers.each { |k,v| logger.info("#{k} #{v}") }
end
end

现在回到 Flex 我们创建一个客户端试验一下效果.

1
2
3
4
5
6
var svc:HTTPService = new HTTPService();
svc.method = "POST";
svc.url = "/users/1";
svc.headers = { HTTP_X_HTTP_METHOD_OVERRIDE:"PUT", Accept:"application/xml" };
svc.request = { user:{ age:30 } };
svc.send();

结果有点长, 删减之后你会发现类似下面这样的内容:

Processing UsersController#create to xml (for 127.0.0.1 at 2009-03-22 11:02:04) [POST]
HTTP_HTTP_X_HTTP_METHOD_OVERRIDE PUT

呃… 为什么会多出个 HTTP_ 前缀来呢? 这个问题留给你来解答吧, 我也实在是说不清楚.

不过没关系, 问题已经明朗了, 重新设计一下我们的消息头就可以了.

1
2
3
4
5
6
7
8
9
10
11
12
13
// 首先创建服务代理, 这里只是个例子, 实例上你可能更喜欢用 Mxml.
var svc:HTTPService = new HTTPService();
// 只能是 POST, 不要被其他那些参数给骗了.
svc.method = "POST";
// 朝用户 ID 为 1 的资源发出 PUT 动作
svc.url = "/users/1";
// 既然不知道哪里来了个 HTTP_ 前缀, 那我们就少打5个字母好了.
// Accept 消息头可以使我们的 url 不必以 .xml 结尾来说明 format.
svc.headers = { X_HTTP_METHOD_OVERRIDE:"PUT", Accept:"application/xml" };
// 请求的内容是你想要更新的字段, 这里视情况而定, 也可以直接写 xml.
svc.request = { user:{ age:30 } };
// 发射!!!
svc.send();

结果:

Processing UsersController#update to xml (for 127.0.0.1 at 2009-03-22 11:05:13) [PUT]
Parameters: {"id"=>"1"}
.......
HTTP_X_HTTP_METHOD_OVERRIDE PUT
.......
    User Load (0.0ms) SELECT * FROM "users" WHERE ("users"."id" = 1)
    User Update (2.0ms) UPDATE "users" SET "age" = 30, "updated_at" = '2009-03-22 03:05:13' WHERE "id" = 1
Completed in 164ms (View: 4, DB: 2) | 200 OK [http://192.168.0.1/users/1]

OK~ 看来 Rails 已经收到我们的信号, 并且正确识别了我们想要的动作 PUT. 其他类似的动作可以由我们自由发挥了, 再也不用受 _method 的牵拌.

嗯, 如果你也是一个像我一样的在技术领域无聊的完美主义者, 但愿这个结果能够令你满意.