↓ Archives ↓

Archive → May, 2010

问题解决笔记三则

改用 homebrew 作为包管理工具

改用了 homebrew 作为包管理工具。 @jjgod 有一篇对 homebrew 的介绍,不过文中说的安装方法有点繁琐;我本就是想打算用 homebrew 来管理这些工具的。

官方提供了一个 Ruby 安装脚本,因此也可以通过这个方法进行安装:

ruby -e "$(curl http://gist.github.com/raw/323731/install_homebrew.rb)"

由于 homebrew 的哲学之一,就是避免使用 sudo,将程序的 owner 更改为 root,所以需要更改默认安装目录的权限来避免因为权限问题而出现的操作失败:

sudo chown -R `whoami` /usr/local

我是完整卸载 MacPorts 后才安装的 homebrew 的,自然得把缺的东西的装回来,倒是得先把一些工具装回来:

brew install git wget rsync

至于 Mercurial,homebrew 给出一个提示让我使用 pip 安装:

brew install pip && pip install mercurial

MySQL 无法启动

由于改用了 homebrew 作为包管理工具,原来 MacPorts 安装的 MySQL 得重新安装,但安装结束后却得到了一个错误,ERROR! Manager of pid-file quit without updating file,求救于 Google 大神,发现是 MacPorts 卸载时没有清理干净 MySQL 进程导致的问题killall mysql,然后进行剩余步骤:

mysql_install_db
launchctl unload -w ~/Library/LaunchAgents/com.mysql.mysqld.plist
cp /usr/local/Cellar/mysql/5*/com.mysql.mysqld.plist ~/Library/LaunchAgents
launchctl load -w ~/Library/LaunchAgents/com.mysql.mysqld.plist

还得给 .profile 文件加上 alias,alias mysqlctl=”/usr/local/Cellar/mysql/5.1.47/share/mysql/mysql.server”,保存,source ~/.profile,然后 mysqlctl start,搞定。

MacVim 的配置问题

用上了 fugitive.vim 才知道 MacVim 的 $PATH 是有问题的,打开 Preferences,激活 Launch Vim processes in a login shell 这个选项,打开一个新的 MacVim 窗口,:!echo $PATH,问题就这样解决了。

一次项目流产的小结

由于只得到一个模糊的答复,但我还是得先承认目前的状况:这个项目流产了,我是这个项目流产的最大关系人。

个人的各种问题和原因,我不多说,我现在只想说技术相关的。

这是来自 MySpace.cn/9911 的项目,做一个 Firefox 的插件,类似 Twitterfox,现在叫做 Echofon 的那个小东西。Firefox 的插件所使用的技术难度不大,JavaScript + XUL,再加点 CSS,很简单,打包成为 xpi 的时候可能还需要知道怎么用 bash,官方有一个自动打包的脚本,但有问题,需要自己修一修。一个对前端熟悉的开发者,再加点 Linux 平台下工作的经验,大致也有足够的基础开始开发了。

单元测试

我并非 quick and dirty 类型的开发者,如果我看代码的结构或者架构有点不爽了,我会小部分、甚至重构整个程序。对此,一个有效的 Unit Test 是最重要的保证。可惜,我没有写 Unit Test。JavaScript 有好些 Unit Test 框架,都是针对 Web 项目的,基于 HTML 的,并非 XUL 的。实际上,并没有一个用于单元测试的工具。当时我曾想过自己做一个简陋的,但终究没有做起来。基于浏览器的测试框架,大致可以通过 F5 刷新来完成测试,而基于 XUL 的,似乎只能通过手动点击更新 chrome 的按钮(通过开发辅助插件)。

我想过一个基于 Python 方案的方案, SpiderMonkey 有 Python 类库的实现。但是这充其量只能算是一个 JavaScript Parser,做语法检测还行,做单元测试是不足够的。

或者,会存在一个足够好的方案来达到自动化测试的目的,但我没有在项目初期做好一个完整的测试方案,若是想着做一个不断完善的东西,一个不断在重构中成长的软件(姑且算作是吧),没有完善的自动化测试方案,简直是笑话。达到工业级质量的软件,必须有单元测试作为后盾。

开发环境、版本控制和自动构建脚本

在项目初期,开发环境是 Windows XP,中途曾转换到 Ubuntu,也有短时间地在公司的 Mac 上进行开发(主要是界面的调整),但开发环境的不停变更,对项目开发,铁定是有影响的。先不说开发环境的配置需要大量的时间,光是一个小问题就足够烦恼了:自动构建脚本。

如果真的存在自动构建脚本,我想,率先需要测试的,肯定是这个东西。而且,这个东西还得能够自动侦测开发环境的不同而出现的差异,自动选择方案。bash shell 我一直觉得是一个很神奇很强大的东西,组合各个小工具写成的 script 可以很强大,可惜,这并不等于迁移到另一机器上就一定能够顺利运行。这也并非说这不好,至少,需要考虑自动构建脚本的健壮性问题。

至于版本控制,我认为我是弄得一塌糊涂的,一个像是在进行版本控制的开发过程;由于开发机的不固定,有点像是把 git 当作 svn 使用了。对于个人开发者来说,问题本是不大,但作为中央节点的机器硬盘出现了问题──中央节点的代码仓库坏了。当时最新的代码版本在某个开发机上,问题影响不大,但若真的需要版本回滚,或者其他──问题就出现了。这本是一个小问题,但要把 git 用得像 git,而非 svn ──这很重要,十分的重要。

类库

开始之时,XHR 和数据库的封装都是自己实现的。由于我固执地认为,需要把取回的消息本地存一份,所以我曾经有好几天的时间,用于钻研怎么在 Firefox 中用好 SQLite。问题是,在 Firefox 中,SQLite 是一个很奇怪的东西。对于并发的读,SQLite 没有问题。但是对于并发的写,例如多个进程或者线程,以异步的方式,在一个 IO 速度不稳定的设备(互联网)上取回数据,然后写入──这样,SQLite 很容易就会出现 deadlock。另一方面,SQLite API 的使用方法相对怪异(相对于习惯 PHP 所实现的 SQLite 封装的我来说)。虽然最近对于这个东西的开发中,我放弃了使用 SQLite 作为后端储存、甚至说没有储存除了帐号信息以后的数据,实际上也并不需要储存这些数据。

继续说类库。在中段有一些时候,不知道从哪里看到 jQuery 也可以在 XUL 的环境中使用,但实际上,需要修改很多很多的东西才能正常使用,如上文所属,没有单元测试保证,这样的改动就意味着人工测试,也意味着开发成本的增加、开发时间的增加。我勉强改了点,想着改名叫作 gQuery ── g stand for Gecko ── 但结果,也会随着现在这个东西的流产也流产了。

开发难度

可以说,开发这样的浏览器插件,是一个很有趣的挑战,现在回想起来就像跑进一个物种丰富的原始森林中觅食,你没有工具,虽然身边都是丰富的原材料。

先说界面。苏小雨的 CSS 2.1 手册是划时代的产品,这让我们知道了 CSS 2.1 有哪些属性,作用如何,但是整理这个出来也就是一个巨大的工程了。有这样的一个手册,我们可以知道哪个属性可以干点什么,作用是什么。再看看 developer.mozilla.org 上的这个页面:https://developer.mozilla.org/en/CSS_Reference

很不错,列出了很多。那么,试试把 -moz-appearance 找出来。Ctrl + F 是找不出来的,得找 Google,然后,你终于找到一个像样的说明了:https://developer.mozilla.org/en/CSS/-moz-appearance

这足够吗?

Firefox 有一点不好:它能保证同样的 HTML 页面、同样的 CSS 代码在不同平台的 Firefox 下有一致的显示效果,却不能保证同样的 XUL + CSS 代码在不同平台的 Firefox 下有一致的显示效果。若非我所在的公司给我配了 Mac Mini 作为开发机,我怕是怎么也不知道,原来还得针对 Mac 的 Firefox 写 hack。对了,前面说的 -moz-appearance ── 这个属性得好好记住,跨平台的样式问题,首先考虑是不是它的问题就好了,摆弄 border、background 什么的,弄不懂按钮的样式,但加一句 -moz-appearance: none 以后,请随意折腾,像折腾 HTML 一样。

Firefox 在 Linux 下的效率被人诟病,我想着还有 XUL 的原因。scrollbox 是一个很不错的东西,试试鼠标按着 scrollbox 滚动条的下拉按钮不放,同期看看CPU占用率──无论在 Windows 还是 Ubuntu,在同一机器上,CPU 占用率都轻松突破 90%。或者看官的机器好一点,不会出现这么大的一个数字。

现在,我会认为这是一个开发难度很大的项目了。看看 zuosafox,对比它和 Twitterfox 的代码(我敢说 ZuosaFox 是抄袭和改动 TwitterFox 的源码做出来);细看 Twitterfox,他们的功能也不多,功能增加基本上停止了──请看源码架构,当然,没有好的测试工具怕也是原因之一。

小结

写了这么多,也不知道还有什么要写了──现已将近六月,这个项目由开始到现在,已经五个月了,我在其中所犯的错误太多,也不一一细数了。大致总结好了。

  1. 别把开发看得太简单。根据项目需求分析功能,然后调研。项目一上来就要求有这个那个的功能──稍等!先试试完成第一步,一个小小的功能,稳定了、符合了,重构,加功能,测试、重构。TDD 是一个良好的开发习惯。
  2. 即使增加一个小小的功能,对整个程序的架构也会有很大的影响。
  3. 良好的封装,完善的测试,是保持持续重构和优化的基础。
  4. 接上,工若善其事,必先礼其器,要甚至考虑,你所用的工具是否足够,例如单元测试框架,例如开发框架,例如文档和社区。

最后,我想着免不了对 9911 API 说点什么。这是一个有问题的 API,每个 URI,输出的同一个数据集、譬如说,user,都会存在问题,在 URI A 中,user 可能附带了字段 FOO,但在 URI B 的输出中,user 这个数据集就不包含 FOO 这个字段。他们大致没有好好地用 OO 来掩饰他们不良好的封装。试想一个只是写了一条 SQL 语句跑出结果、再扔 json_encode 中、最后把结果返回的脚本。封装不良好的一个问题就是,对测试不良好,但就我的感觉,我认为他们甚至没有单元测试。我希望我这两条推断都是错的。

Thought on Work

年初接了一个工作,但拖至今天仍未能完成,对方告知,“公司或者会砍掉这个项目”。暂缓说更多在这项工作中出现的问题,或者真的要总结就是一篇长长的东西,只说自己的原因:拖沓,然后受困于架构的问题,事情多了就顾此失彼。

昨晚一口气读了 IBM DeveloperWorks 上连载的《演化架构与紧急设计系列》,似乎略有所悟:自己知道 TDD,却从来没有真正身体力行地实践 TDD,这会不会是一个原因呢?

再看这篇,《懂了才去定制》,里面说的一段话:

一个学生如果对要学的东西还没有真正吃透,那么请不要急于寻求突破;一个开发团队如果对将要采用的新方法还没一个完整的认识,那么也请不要盲目定制。

因为你很可能还不懂自己究竟在做什么。

虽然自己读了《硝烟中的Scrum和XP》,如果没有身体力行,没有完整理解,没有尝试,那么这那是在实践敏捷开发呢?

需要更多反思才是。

说到Scrum,倒是想到了一个好处,就是把工作都白纸黑字量化了,分成一个个backlog,不能再说因为可见的变化不存在,那么就是没有完成工作。下午公司的每周例会上,自己的低效受到了批评,但另一方面,我在思考当前项目的问题时,所做的都是背后的处理,再多的代码、改了又改,也只是看不到的变化,而业绩多少的定义,怕是更着重表面上看到的。而自己都是把想法在脑里打转,想一点做一点,遇到问题、需要修改大片代码,然后把时间就浪费在重构和手动测试中。一个可演化的架构,一个更快捷的自动化测试,Test-Driven Development——或者是自己需要的。这话我说得不肯定,但至少是目前的一根救命稻草吧。

在 shell prompt 中显示 git 的变更

看到这样的一个东西,git-prompt

闲话略过不提,大概记下自己应用这东西的笔记。

首先是 checkout 代码:git clone http://github.com/lvv/git-prompt.git ~/src/;这里假设你用户根目录下有一个叫做 src 的文件夹。

在 Linux 中是 ~/.bashrc,在 Mac OS X 下是 ~/.profile,使用趁手的编辑打开这个文件,我这里是 vim ~/.profile,加入这句:source ~/src/git-prompt/git-prompt.sh

至此,安装完成。需要修改配置的话,把 git-prompt.conf 复制到用户根目录下并改名为 .git-prompt.conf,通过修改这个文件进行配置即可。

我不喜欢那么长的一行 prompt,而且还会把你自定义的 PS1 给强制无效化。前一个问题简单修改一下 git-prompt.sh 就可以了,后一个问题就跟整个运作机制有关了。

我对 git-prompt.sh 的修改很简单,将 601 行的

head_local="${head_local+$vcs_color$head_local }"

改为:

head_local="\n${head_local+$vcs_color$head_local }"

将 659 行的

PS1="$colors_reset$rc$head_local$color_who_where$dir_color$cwd$tai l_local$dir_color$prompt_char $colors_reset"

改为:

PS1="\n$color_who_where$dir_color$cwd$tail_local$dir_color$colors_re set$rc$head_local$colors_reset$prompt_char "

完事。