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

2008年1月15日星期二

查看vendor下Rails版本

命令:

script/about


打印出来:

About your application's environment
Ruby version 1.8.5 (i486-linux)
RubyGems version 0.9.4
Rails version 1.2.5
Active Record version 1.15.5
Action Pack version 1.13.5
Action Web Service version 1.2.5
Action Mailer version 1.3.5
Active Support version 1.4.4
Edge Rails revision 547
Application root /home/xxx/project/
Environment development
Database adapter mysql
Database schema version 3


或者:

ruby vendor\rails\railties\bin\rails -v

链接伪类的顺序

必须以特定的顺序来定义样式:link、visited、hover、active
可以简记为:LoVe/HAte

其实还是遵照了样式层叠的规律,后定义的样式覆盖前面定义的样式。因此就要把普通的放在前面,特殊的放在后面。
link既可以是未点击的,又可以是已点击的,还可以是鼠标经过时的样式。如果link放在hover后面,就会覆盖hover的定义。
那把hover放在link后面呢?它不是会覆盖link的定义吗?哈哈,对了,我们就希望它覆盖link的定义,在鼠标经过时修改link的样式。

改进你的CSS习惯 - Improving Your CSS Habits

添加注释 - Add Comments
这个不用说了

命名 - Name Styles Clearly
  • 根据用途而不是展现效果来命名
  • 不要用.redhighlight这样的命名,万一哪天老板让你改成橙色的呢?如果是用来警告或提示错误,不妨用.alert或.error
  • 不要根据位置来命名
  • 不要用.leftsidebar,有一天它完全可能被移到右边去(但是可以用两个类.left单独用来表示布局位置,.sidebar用来表示侧条栏的样式,用的时候就像这样<div class="sidebar left">)
  • 避免含义模糊的命名
  • 就是说不要用无意义的命名,如.s、#s2等,而应该改成.news、#mainNav等。就像程序的变量命名一样,要能够见名知意

用多个类来节约时间 - Use Multiple Classes to Save Time
就像上面举到的<div class="sidebar left">的例子一样,把sidebar共性的样式定义到一起,而把跟布局相关的定义为另一个类。这样,当左右各有一个sidebar的时候,就不需要把sidebar的样式定义两遍了。那样做很不DRY,复制粘贴一遍很容易,但一旦要修改,就要同时改两处地方,很麻烦。

消除浏览器样式冲突 - Eliminating Browser Style Interference
就是去除浏览器默认加的一些样式,如讨厌的默认margin和padding。应该先将这些样式归零。
body, h1, h2, h3, h4, h5, h6, p, ol, ul, form, blockquote {
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5, h6, pre, code {
font-size: 1em;
}
a {
text-decoration: none;
}
a img {
border: none;
}

也可以参考这个:http://www.tantek.com/log/2004/undohtml.css
跟上面的大同小异

优先为当代的浏览器设计 - Design for Contemporary Browsers First
在Firefox, Safari下设计好了后,再来hack臭名昭著的IE6

IE可以识别条件注释,如:
<!--[if IE]>
用来hack IE的bug的样式放在这里
<!--[endif]>

还有针对特定版本IE的,lte表示"less than or euqal to",即IE6或更低版本:
<!--[if lte IE6]
some styles
<!--[endif]>

记住,这些专为IE而使用的hack一定要放到其他样式的后面,从而可以修复(覆盖)之前定义的样式。

2008年1月14日星期一

颜色关键字

aqua(浅绿色)
black(黑色)
blue(蓝色)
fuchsia(紫红色)
gray(灰色)
green(绿色)
lime(石灰色)
maroon(栗色)
navy(海军蓝)
olive(橄榄色)
orange(橙色)
purple(紫色)
red(红色)
silver(银色)
teal(水鸭色)
white(白色)
yellow(黄色)

CSS的层叠规则

  • 一个标签选择器值1分(p {color: red;})
  • 一个类选择器值10分(.cls {color: red;})
  • 一个ID选择器值100分(#som {color: red;})
  • 一个行内样式值1000分(<span style="color:red;">)

一个派生选择器的值是所有列出的选择器值的总和,如:
#banker .byline => 100 + 10 = 110

其他规则:后出现的样式胜出,如
p a.email { color: blue; }
p.byline a { color: red; }
则链接的颜色应该为红色。

2008年1月13日星期日

六种用ruby调用执行shell命令的方法(转)

碰到需要调用操作系统shell命令的时候,Ruby为我们提供了六种完成任务的方法:
1.Exec方法:
Kernel#exec方法通过调用指定的命令取代当前进程:
例子:
$ irb
>> exec 'echo "hello $HOSTNAME"'
hello nate.local
$
值得注意的是,exec方法用echo命令来取代了irb进程从而退出了irb。主要的缺点是,你无法从你的ruby脚本里知道这个命令是成功还是失败。
2.System方法。
Kernel#system方法操作命令同上, 但是它是运行一个子shell来避免覆盖当前进程。如果命令执行成功则返回true,否则返回false。
$ irb
>> system 'echo "hello $HOSTNAME"'
hello nate.local
=> true
>> system 'false'
=> false
>> puts $?
256
=> nil
>>
3.反引号(Backticks,Esc键下面那个键)
$ irb
>> today = `date`
=> "Mon Mar 12 18:15:35 PDT 2007n"
>> $?
=> #
>> $?.to_i
=> 0
这种方法是最普遍的用法了。它也是运行在一个子shell中。
4.IO#popen
$ irb
>> IO.popen("date") { |f| puts f.gets }
Mon Mar 12 18:58:56 PDT 2007
=> nil
5.open3#popen3
$ irb
>> stdin, stdout, stderr = Open3.popen3('dc')
=> [#, #, #]
>> stdin.puts(5)
=> nil
>> stdin.puts(10)
=> nil
>> stdin.puts("+")
=> nil
>> stdin.puts("p")
=> nil
>> stdout.gets
=> "15n"
6.Open4#popen4
$ irb
>> require "open4"
=> true
>> pid, stdin, stdout, stderr = Open4::popen4 "false"
=> [26327, #, #, #]
>> $?
=> nil
>> pid
=> 26327
>> ignored, status = Process::waitpid2 pid
=> [26327, #]
>> status.to_i
=> 256

原文:
http://blackanger.blog.51cto.com/140924/43730

attr_accessor_with_default

实现
activesupport/lib/active_support/core_ext/module/attr_accessor_with_default.rb
测试
activesupport/test/core_ext/module/attr_accessor_with_default_test.rb

实现方法:
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__)
def #{sym}=(value)
class << self; attr_reader :#{sym} end
@#{sym} = value
end
EVAL
end

define_method,即定义一个方法,方法名为#{sym},另外接受一个block作为其方法体,此处为*直接*返回一个默认值。
这里就是在定义get方法。
怪就怪在后面的set方法的定义,为什么还要先写一行 class << self; attr_reader :#{sym} end 呢?
因为前面的get方法是直接返回一个默认值,并没有生成一个名为@#{sym}的实例变量,所以这一行的作用是用Ruby提供的attr_reader方法,来重新生成一个get方法,这个新的get方法里,自然就有名为@#{sym}的实例变量了。
然后再是标准的set方法体:@#{sym} = value
三个方法的生成过程为:
由 define_method(sym, block_given? ? block : Proc.new { default }) 生成:
def foo
default
end
由class << self; attr_reader :#{sym} end 生成:
def foo
@foo
end
def foo=(value)
@foo = value
end
所以一旦调用了set方法,前面的get方法被后面的get方法覆盖。

为了证实,可以修改实现文件和测试文件:
def attr_accessor_with_default(sym, default = nil, &block)
raise 'Default value or block required' unless !default.nil? || block
define_method(sym, block_given? ? block : Proc.new { default })
module_eval(<<-EVAL, __FILE__, __LINE__)
def #{sym}=(value)
class << self; attr_reader :#{sym} end
@#{sym} = value
# @#{sym} = value # 注释掉该行,使得set方法不赋值,但仍然生成一个新的get方法,并生成名为@#{sym}的实例变量
end
EVAL
end
def test_default_arg
@target.attr_accessor_with_default :foo, :bar
p @instance.foo # 执行set方法前,调用get方法
assert_equal(:bar, @instance.foo)
@instance.foo = 'foo' # 此处赋值为'foo',而非原来的nil
p @instance.foo # 执行set方法后,再调用get方法
# assert_nil(@instance.foo) # 注释掉该行,以免因为我们的修改而使测试出错
end

运行测试,打印出结果:

...:bar
nil...


也就是说,第一次调用get方法,是按照预想的返回一个默认值 :bar,此时还是旧的get方法。第二次调用时,返回的是新的get方法里的实例变量@foo,而@foo并未被赋值,所以返回值是nil(前一行的@instance.foo = 'foo'并没有起到赋值的作用,因为我们在实现文件里已经把相应的行注释掉了)

HashWithIndifferentAccess

Rails里,常用params[:sym]来取页面的参数,其实从页面上传过来的都是字符串(hash的key和value都是字符串),Rails为了让我们能够按照习惯用symbol来取值,就做了一些改变
在activesupport/lib/active_support/core_ext/hash/indifferent_access.rb里。
所有的key都被强制转换成了string,也就是说:
h = HashWithIndifferentAccess.new  #=>  {}
h[:a] = "a" #=> "a"
h #=> {"a" => "a"}

具体的转换体现在下面的代码:
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
alias_method :regular_update, :update unless method_defined?(:regular_update)

def []=(key, value)
regular_writer(convert_key(key), convert_value(value))
end

def update(other_hash)
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
self
end
先给父类的[]=方法和update方法取个别名,然后再覆盖这两个方法。

然而进一步查看其源代码,却发现它对stringify_keys!和symbolize_keys!这两个方法的测试不正确,没有覆盖到。因为我把对应现代码注释掉,再运行测试,居然都通过了,没有任何报错提示。
源代码如下:
def stringify_keys!; self end
def symbolize_keys!; self end
测试代码如下:
def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash
h = HashWithIndifferentAccess.new
h[:first] = 1
h.stringify_keys!
assert_equal 1, h['first']
h = HashWithIndifferentAccess.new
h['first'] = 1
h.symbolize_keys!
assert_equal 1, h[:first]
end

应该改为:
def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash
h = HashWithIndifferentAccess.new
h[:first] = 1
h.stringify_keys!
assert_equal 1, h['first']
h = HashWithIndifferentAccess.new
h['first'] = 1
h.symbolize_keys!
assert_not_equal :first, h.index(1)
# assert_equal 1, h[:first]
end

个人认为stringify_keys!方法在这里是无法测试的,因为该类里key全都转换成string了,那么这个"Destructively convert all keys to strings."(父类方法的注释说明)的方法,其调用前后,对象没有任何改变,无法编写对应的测试。这里覆盖父类方法,可能的原因是基于效率的考虑,让其不再调用父类的stringify_keys!方法(因为已经全都是string了,再调用一次,再判断一次是浪费)。

还有个问题,HashWithIndifferentAccess类只保证了key都是按string来存储的,但为什么h[:a]这样用symbol还能取到呢?它并没有覆盖父类的[]方法。
经过跟同事的讨论,有了结果:
def default(key = nil)
if key.is_a?(Symbol) && include?(key = key.to_s)
self[key]
else
super
end
end
它覆盖了default方法,而ruby实现里[]方法会去调用default方法。具体的ruby底层实现机制我并不了解,只是通过改源代码和其测试代码,发现了default方法跟[]方法相关。

Steve Jobs在Stanford2005毕业典礼上的演讲(转)

This is the text of the Commencement address by Steve Jobs, CEO of Apple Computer and of Pixar Animation Studios, delivered on June 12, 2005.

I am honored to be with you today at your commencement from one of the finest universities in the world. I never graduated from college. Truth be told, this is the closest I've ever gotten to a college graduation. Today I want to tell you three stories from my life. That's it. No big deal. Just three stories.

The first story is about connecting the dots.

I dropped out of Reed College after the first 6 months, but then stayed around as a drop-in for another 18 months or so before I really quit. So why did I drop out?

It started before I was born. My biological mother was a young, unwed college graduate student, and she decided to put me up for adoption. She felt very strongly that I should be adopted by college graduates, so everything was all set for me to be adopted at birth by a lawyer and his wife. Except that when I popped out they decided at the last minute that they really wanted a girl. So my parents, who were on a waiting list, got a call in the middle of the night asking: "We have an unexpected baby boy; do you want him?" They said: "Of course." My biological mother later found out that my mother had never graduated from college and that my father had never graduated from high school. She refused to sign the final adoption papers. She only relented a few months later when my parents promised that I would someday go to college.

And 17 years later I did go to college. But I naively chose a college that was almost as expensive as Stanford, and all of my working-class parents' savings were being spent on my college tuition. After six months, I couldn't see the value in it. I had no idea what I wanted to do with my life and no idea how college was going to help me figure it out. And here I was spending all of the money my parents had saved their entire life. So I decided to drop out and trust that it would all work out OK. It was pretty scary at the time, but looking back it was one of the best decisions I ever made. The minute I dropped out I could stop taking the required classes that didn't interest me, and begin dropping in on the ones that looked interesting.

It wasn't all romantic. I didn't have a dorm room, so I slept on the floor in friends' rooms, I returned coke bottles for the 5¢ deposits to buy food with, and I would walk the 7 miles across town every Sunday night to get one good meal a week at the Hare Krishna temple. I loved it. And much of what I stumbled into by following my curiosity and intuition turned out to be priceless later on. Let me give you one example:

Reed College at that time offered perhaps the best calligraphy instruction in the country. Throughout the campus every poster, every label on every drawer, was beautifully hand calligraphed. Because I had dropped out and didn't have to take the normal classes, I decided to take a calligraphy class to learn how to do this. I learned about serif and san serif typefaces, about varying the amount of space between different letter combinations, about what makes great typography great. It was beautiful, historical, artistically subtle in a way that science can't capture, and I found it fascinating.

None of this had even a hope of any practical application in my life. But ten years later, when we were designing the first Macintosh computer, it all came back to me. And we designed it all into the Mac. It was the first computer with beautiful typography. If I had never dropped in on that single course in college, the Mac would have never had multiple typefaces or proportionally spaced fonts. And since Windows just copied the Mac, its likely that no personal computer would have them. If I had never dropped out, I would have never dropped in on this calligraphy class, and personal computers might not have the wonderful typography that they do. Of course it was impossible to connect the dots looking forward when I was in college. But it was very, very clear looking backwards ten years later.

Again, you can't connect the dots looking forward; you can only connect them looking backwards. So you have to trust that the dots will somehow connect in your future. You have to trust in something — your gut, destiny, life, karma, whatever. This approach has never let me down, and it has made all the difference in my life.

My second story is about love and loss.

I was lucky — I found what I loved to do early in life. Woz and I started Apple in my parents garage when I was 20. We worked hard, and in 10 years Apple had grown from just the two of us in a garage into a $2 billion company with over 4000 employees. We had just released our finest creation — the Macintosh — a year earlier, and I had just turned 30. And then I got fired. How can you get fired from a company you started? Well, as Apple grew we hired someone who I thought was very talented to run the company with me, and for the first year or so things went well. But then our visions of the future began to diverge and eventually we had a falling out. When we did, our Board of Directors sided with him. So at 30 I was out. And very publicly out. What had been the focus of my entire adult life was gone, and it was devastating.

I really didn't know what to do for a few months. I felt that I had let the previous generation of entrepreneurs down - that I had dropped the baton as it was being passed to me. I met with David Packard and Bob Noyce and tried to apologize for screwing up so badly. I was a very public failure, and I even thought about running away from the valley. But something slowly began to dawn on me — I still loved what I did. The turn of events at Apple had not changed that one bit. I had been rejected, but I was still in love. And so I decided to start over.

I didn't see it then, but it turned out that getting fired from Apple was the best thing that could have ever happened to me. The heaviness of being successful was replaced by the lightness of being a beginner again, less sure about everything. It freed me to enter one of the most creative periods of my life.

During the next five years, I started a company named NeXT, another company named Pixar, and fell in love with an amazing woman who would become my wife. Pixar went on to create the worlds first computer animated feature film, Toy Story, and is now the most successful animation studio in the world. In a remarkable turn of events, Apple bought NeXT, I returned to Apple, and the technology we developed at NeXT is at the heart of Apple's current renaissance. And Laurene and I have a wonderful family together.

I'm pretty sure none of this would have happened if I hadn't been fired from Apple. It was awful tasting medicine, but I guess the patient needed it. Sometimes life hits you in the head with a brick. Don't lose faith. I'm convinced that the only thing that kept me going was that I loved what I did. You've got to find what you love. And that is as true for your work as it is for your lovers. Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work. And the only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle. As with all matters of the heart, you'll know when you find it. And, like any great relationship, it just gets better and better as the years roll on. So keep looking until you find it. Don't settle.

My third story is about death.

When I was 17, I read a quote that went something like: "If you live each day as if it was your last, someday you'll most certainly be right." It made an impression on me, and since then, for the past 33 years, I have looked in the mirror every morning and asked myself: "If today were the last day of my life, would I want to do what I am about to do today?" And whenever the answer has been "No" for too many days in a row, I know I need to change something.

Remembering that I'll be dead soon is the most important tool I've ever encountered to help me make the big choices in life. Because almost everything — all external expectations, all pride, all fear of embarrassment or failure - these things just fall away in the face of death, leaving only what is truly important. Remembering that you are going to die is the best way I know to avoid the trap of thinking you have something to lose. You are already naked. There is no reason not to follow your heart.

About a year ago I was diagnosed with cancer. I had a scan at 7:30 in the morning, and it clearly showed a tumor on my pancreas. I didn't even know what a pancreas was. The doctors told me this was almost certainly a type of cancer that is incurable, and that I should expect to live no longer than three to six months. My doctor advised me to go home and get my affairs in order, which is doctor's code for prepare to die. It means to try to tell your kids everything you thought you'd have the next 10 years to tell them in just a few months. It means to make sure everything is buttoned up so that it will be as easy as possible for your family. It means to say your goodbyes.

I lived with that diagnosis all day. Later that evening I had a biopsy, where they stuck an endoscope down my throat, through my stomach and into my intestines, put a needle into my pancreas and got a few cells from the tumor. I was sedated, but my wife, who was there, told me that when they viewed the cells under a microscope the doctors started crying because it turned out to be a very rare form of pancreatic cancer that is curable with surgery. I had the surgery and I'm fine now.

This was the closest I've been to facing death, and I hope its the closest I get for a few more decades. Having lived through it, I can now say this to you with a bit more certainty than when death was a useful but purely intellectual concept:

No one wants to die. Even people who want to go to heaven don't want to die to get there. And yet death is the destination we all share. No one has ever escaped it. And that is as it should be, because Death is very likely the single best invention of Life. It is Life's change agent. It clears out the old to make way for the new. Right now the new is you, but someday not too long from now, you will gradually become the old and be cleared away. Sorry to be so dramatic, but it is quite true.

Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma — which is living with the results of other people's thinking. Don't let the noise of others' opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition. They somehow already know what you truly want to become. Everything else is secondary.

When I was young, there was an amazing publication called The Whole Earth Catalog, which was one of the bibles of my generation. It was created by a fellow named Stewart Brand not far from here in Menlo Park, and he brought it to life with his poetic touch. This was in the late 1960's, before personal computers and desktop publishing, so it was all made with typewriters, scissors, and polaroid cameras. It was sort of like Google in paperback form, 35 years before Google came along: it was idealistic, and overflowing with neat tools and great notions.

Stewart and his team put out several issues of The Whole Earth Catalog, and then when it had run its course, they put out a final issue. It was the mid-1970s, and I was your age. On the back cover of their final issue was a photograph of an early morning country road, the kind you might find yourself hitchhiking on if you were so adventurous. Beneath it were the words: "Stay Hungry. Stay Foolish." It was their farewell message as they signed off. Stay Hungry. Stay Foolish. And I have always wished that for myself. And now, as you graduate to begin anew, I wish that for you.

Stay Hungry. Stay Foolish.

Thank you all very much

http://news-service.stanford.edu/news/2005/june15/jobs-061505.html


  我今天很荣幸能和你们一起参加毕业典礼,斯坦福大学是世界上最好的大学之一。我从来没有从大学中毕业。说实话,今天也许是在我的生命中离大学毕业最近的一天了。今天我想向你们讲述我生活中的三个故事。不是什么大不了的事情,只是三个故事而已。
  
  第一个故事是关于如何把生命中的点点滴滴串连起来。
  
  我在Reed大学读了六个月之后就退学了,但是在十八个月以后——我真正的作出退学决定之前,我还经常去学校。我为什么要退学呢?
  
  故事从我出生的时候讲起。我的亲生母亲是一个年轻的,没有结婚的大学毕业生。她决定让别人收养我, 她十分想让我被大学毕业生收养。所以在我出生的时候,她已经做好了一切的准备工作,能使得我被一个律师和他的妻子所收养。但是她没有料到,当我出生之后, 律师夫妇突然决定他们想要一个女孩。所以我的生养父母(他们还在我亲生父母的观察名单上)突然在半夜接到了一个电话:“我们现在这儿有一个不小心生出来的男婴,你们想要他吗?”他们回答道: “当然!”但是我亲生母亲随后发现,我的养母从来没有上过大学,我的父亲甚至从没有读过高中。她拒绝签这个收养合同。只是在几个月以后,我的父母答应她一定要让我上大学,那个时候她才同意。
  
  在十七岁那年,我真的上了大学。但是我很愚蠢的选择了一个几乎和你们斯坦福大学一样贵的学校, 我父母还处于蓝领阶层,他们几乎把所有积蓄都花在了我的学费上面。在六个月后, 我已经看不到其中的价值所在。我不知道我想要在生命中做什么,我也不知道大学能帮助我找到怎样的答案。但是在这里,我几乎花光了我父母这一辈子的所有积蓄。所以我决定要退学,我觉得这是个正确的决定。不能否认,我当时确实非常的害怕, 但是现在回头看看,那的确是我这一生中最棒的一个决定。在我做出退学决定的那一刻, 我终于可以不必去读那些令我提不起丝毫兴趣的课程了。然后我还可以去修那些看起来有点意思的课程。

  但是这并不是那么罗曼蒂克。我失去了我的宿舍,所以我只能在朋友房间的地板上面睡觉,我去捡5美分的可乐瓶子,仅仅为了填饱肚子, 在星期天的晚上,我需要走七英里的路程,穿过这个城市到Hare Krishna寺庙(注:位于纽约Brooklyn下城),只是为了能吃上饭——这个星期唯一一顿好一点的饭。但是我喜欢这样。

  我跟着我的直觉和好奇心走, 遇到的很多东西,此后被证明是无价之宝。让我给你们举一个例子吧:
  
  Reed大学在那时提供也许是全美最好的美术字课程。在这个大学里面的每个海报, 每个抽屉的标签上面全都是漂亮的美术字。因为我退学了, 不必去上正规的课程, 所以我决定去参加这个课程,去学学怎样写出漂亮的美术字。我学到了san serif 和serif字体, 我学会了怎么样在不同的字母组合之中改变空格的长度, 还有怎么样才能作出最棒的印刷式样。那是一种科学永远不能捕捉到的、美丽的、真实的艺术精妙, 我发现那实在是太美妙了。
  
  当时看起来这些东西在我的生命中,好像都没有什么实际应用的可能。但是十年之后,当我们在设计第一台Macintosh电脑的时候,就不是那样了。我把当时我学的那些家伙全都设计进了Mac。那是第一台使用了漂亮的印刷字体的电脑。如果我当时没有退学, 就不会有机会去参加这个我感兴趣的美术字课程, Mac就不会有这么多丰富的字体,以及赏心悦目的字体间距。因为Windows只是抄袭了Mac,所以现在个人电脑就不会有现在这么美妙的字型了。
  
  当然我在大学的时候,还不可能把从前的点点滴滴串连起来,但是当我十年后回顾这一切的时候,真的豁然开朗了。

  再次说明的是,你在向前展望的时候不可能将这些片断串连起来;你只能在回顾的时候将点点滴滴串连起来。所以你必须相信这些片断会在你未来的某一天串连起来。你必须要相信某些东西:你的勇气、目的、生命、因缘。这个过程从来没有令我失望,只是让我的生命更加地与众不同而已。
  
  我的第二个故事是关于爱和损失的。
  
  我非常幸运, 因为我在很早的时候就找到了我钟爱的东西。Woz和我在二十岁的时候就在父母的车库里面开创了苹果公司。我们工作得很努力, 十年之后, 这个公司从那两个车库中的穷光蛋发展到了超过四千名的雇员、价值超过二十亿的大公司。在公司成立的第九年,我们刚刚发布了最好的产品,那就是 Macintosh。我也快要到三十岁了。在那一年, 我被炒了鱿鱼。你怎么可能被你自己创立的公司炒了鱿鱼呢? 嗯,在苹果快速成长的时候,我们雇用了一个很有天分的家伙和我一起管理这个公司, 在最初的几年,公司运转的很好。但是后来我们对未来的看法发生了分歧, 最终我们吵了起来。当争吵不可开交的时候, 董事会站在了他的那一边。所以在三十岁的时候, 我被炒了。在这么多人的眼皮下我被炒了。在而立之年,我生命的全部支柱离自己远去, 这真是毁灭性的打击。
  
  在最初的几个月里,我真是不知道该做些什么。我把从前的创业激情给丢了, 我觉得自己让与我一同创业的人都很沮丧。我和David Pack和Bob Boyce见面,并试图向他们道歉。我把事情弄得糟糕透顶了。但是我渐渐发现了曙光, 我仍然喜爱我从事的这些东西。苹果公司发生的这些事情丝毫的没有改变这些, 一点也没有。我被驱逐了,但是我仍然钟爱它。所以我决定从头再来。
  
  我当时没有觉察, 但是事后证明, 从苹果公司被炒是我这辈子发生的最棒的事情。因为,作为一个成功者的极乐感觉被作为一个创业者的轻松感觉所重新代替: 对任何事情都不那么特别看重。这让我觉得如此自由, 进入了我生命中最有创造力的一个阶段。

  在接下来的五年里, 我创立了一个名叫NeXT的公司, 还有一个叫Pixar的公司, 然后和一个后来成为我妻子的优雅女人相识。Pixar 制作了世界上第一个用电脑制作的动画电影——“玩具总动员”,Pixar现在也是世界上最成功的电脑制作工作室。在后来的一系列运转中,Apple收购了 NeXT, 然后我又回到了Apple公司。我们在NeXT发展的技术在Apple的复兴之中发挥了关键的作用。我还和Laurence 一起建立了一个幸福的家庭。
  
  我可以非常肯定,如果我不被Apple开除的话, 这其中一件事情也不会发生的。这个良药的味道实在是太苦了,但是我想病人需要这个药。有些时候, 生活会拿起一块砖头向你的脑袋上猛拍一下。不要失去信心。我很清楚唯一使我一直走下去的,就是我做的事情令我无比钟爱。你需要去找到你所爱的东西。对于工作是如此, 对于你的爱人也是如此。你的工作将会占据生活中很大的一部分。你只有相信自己所做的是伟大的工作, 你才能怡然自得。如果你现在还没有找到, 那么继续找、不要停下来、全心全意的去找, 当你找到的时候你就会知道的。就像任何真诚的关系, 随着岁月的流逝只会越来越紧密。所以继续找,直到你找到它,不要停下来!
  
  我的第三个故事是关于死亡的。
  
  当我十七岁的时候, 我读到了一句话:“如果你把每一天都当作生命中最后一天去生活的话,那么有一天你会发现你是正确的。”这句话给我留下了深刻的印象。从那时开始,过了33 年,我在每天早晨都会对着镜子问自己:“如果今天是我生命中的最后一天, 你会不会完成你今天想做的事情呢?”当答案连续很多次被给予“不是”的时候, 我知道自己需要改变某些事情了。
  
  “记住你即将死去”是我一生中遇到的最重要箴言。它帮我指明了生命中重要的选择。因为几乎所有的事情, 包括所有的荣誉、所有的骄傲、所有对难堪和失败的恐惧,这些在死亡面前都会消失。我看到的是留下的真正重要的东西。你有时候会思考你将会失去某些东西, “记住你即将死去”是我知道的避免这些想法的最好办法。你已经赤身裸体了, 你没有理由不去跟随自己的心一起跳动。
  
  大概一年以前, 我被诊断出癌症。我在早晨七点半做了一个检查, 检查清楚的显示在我的胰腺有一个肿瘤。我当时都不知道胰腺是什么东西。医生告诉我那很可能是一种无法治愈的癌症, 我还有三到六个月的时间活在这个世界上。我的医生叫我回家, 然后整理好我的一切, 那就是医生准备死亡的程序。那意味着你将要把未来十年对你小孩说的话在几个月里面说完.;那意味着把每件事情都搞定, 让你的家人会尽可能轻松的生活;那意味着你要说“再见了”。
  
  我整天和那个诊断书一起生活。后来有一天早上我作了一个活切片检查,医生将一个内窥镜从我的喉咙伸进去,通过我的胃, 然后进入我的肠子, 用一根针在我的胰腺上的肿瘤上取了几个细胞。我当时很镇静,因为我被注射了镇定剂。但是我的妻子在那里, 后来告诉我,当医生在显微镜地下观察这些细胞的时候他们开始尖叫, 因为这些细胞最后竟然是一种非常罕见的可以用手术治愈的胰腺癌症。我做了这个手术, 现在我痊愈了。
  
  那是我最接近死亡的时候, 我还希望这也是以后的几十年最接近的一次。从死亡线上又活了过来, 死亡对我来说,只是一个有用但是纯粹是知识上的概念的时候,我可以更肯定一点地对你们说:

  没有人愿意死, 即使人们想上天堂, 人们也不会为了去那里而死。但是死亡是我们每个人共同的终点。从来没有人能够逃脱它。也应该如此。因为死亡就是生命中最好的一个发明。它将旧的清除以便给新的让路。你们现在是新的, 但是从现在开始不久以后, 你们将会逐渐的变成旧的然后被清除。我很抱歉这很戏剧性, 但是这十分的真实。
  
  你们的时间很有限, 所以不要将他们浪费在重复其他人的生活上。不要被教条束缚,那意味着你和其他人思考的结果一起生活。不要被其他人喧嚣的观点掩盖你真正的内心的声音。还有最重要的是, 你要有勇气去听从你直觉和心灵的指示——它们在某种程度上知道你想要成为什么样子,所有其他的事情都是次要的。
  
  当我年轻的时候, 有一本叫做“整个地球的目录”振聋发聩的杂志,它是我们那一代人的圣经之一。它是一个叫Stewart Brand的家伙在离这里不远的Menlo Park书写的, 他象诗一般神奇地将这本书带到了这个世界。那是六十年代后期, 在个人电脑出现之前, 所以这本书全部是用打字机,、剪刀还有偏光镜制造的。有点像用软皮包装的google, 在google出现三十五年之前:这是理想主义的,其中有许多灵巧的工具和伟大的想法。

  Stewart和他的伙伴出版了几期的“整个地球的目录”,当它完成了自己使命的时候, 他们做出了最后一期的目录。那是在七十年代的中期, 我正是你们的年纪。在最后一期的封底上是清晨乡村公路的照片(如果你有冒险精神的话,你可以自己找到这条路的),在照片之下有这样一段话:“保持饥饿,保持愚蠢。”这是他们停止了发刊的告别语。“保持饥饿,保持愚蠢。”我总是希望自己能够那样,现在, 在你们即将毕业,开始新的旅程的时候, 我也希望你们能这样:
  
  保持饥饿,保持愚蠢。
  
  非常感谢你们

RadRails Shortcut

使用RadRails时自定义的一些快捷键,做备忘。

这些快捷键的设定我没有参考其他任何编辑器,只是根据自己的使用,凭感觉,尽量让手不移开键盘的基准位置(ASDF JKL;),尽量使用Alt键(Ctrl键太远了,而且要用小指去按),尽量不用上下左右方向键和Home、End、Page Up、Page Down键(因为要离开基准位置)。
本来很想把Caps Lock键废掉,设置为Ruby Content Assist功能(该功能在TextMate里是Tab键,但在RadRails里Tab键不能再设定其他功能,否则原有制表符功能会废掉),但不知道该怎么设置。其实Tab键是最方便的一个键,退而求其次Caps Lock键也是第二方便的键,遗憾的是这两个键都用不了。
另外很重要的一点就是,在定义新快捷键的同时,原有的常用的快捷键也不应该取消,如Ctrl + Z、Ctrl + Y、Ctrl + X、Ctrl + C、Ctrl + V、Ctrl + S,一是可以给自己过渡,而是跟别人pair programming的时候,不会对你的键盘行为太陌生。
Close All        Alt + B W (custom) or Shift + Ctrl + W
Max/Min Editor Alt + 1 (custom) or Ctrl + 1 (custom)
Open Resource Alt + 2 (custom) or Ctrl + 2 (custom)
Next Editor Alt + 3 (custom)
Search Alt + 4 (custom)
Switch to test Alt + 5 (custom)
Switch to model Alt + 6 (custom)
Switch to view Alt + 7 (custom)
Switch to contraller Alt + 8 (custom)
Save Alt + S (custom)
Close Alt + W (custom)
Undo Alt + E (custom)
Redo Alt + R (custom)
Line Up Alt + I (custom)
Line Down Alt + K (custom)
Previous Column Alt + J (custom)
Next Column Alt + L (custom)
Line Start Alt + H (custom)
Line End Alt + ; (custom)
Page UP Alt + U (custom)
Page Down Alt + N (custom)
Previous Word Alt + O (custom)
Next Word Alt + P (custom)
Text Start Alt + 9 (custom) or Alt + B I (custom)
Text End Alt + 0 (custom) or Alt + B K (custom)
Duplicate Lines Alt + T (custom)
Cut Lines Alt + Y (custom)
Delete Lines Alt + D (custom)
Move Line Up Alt + ↑ (custom)
Move Line Down Alt + ↓ (custom)
Select Line Start Alt + [ (custom) or Alt + B J (custom) or Ctrl + H (custom)
Select Line End Alt + ] (custom) or Alt + B L (custom) or Ctrl + ; (custom)
Select Previous Column Alt + < (custom) or Ctrl + J (custom) Select Next Column Alt + > (custom) or Ctrl + L (custom)
Select Line Up Ctrl + I (custom)
Select Line Down Ctrl + K (custom)
Select Next Word Alt + G (custom)
Scroll Line Up Ctrl + ↑
Scroll Line Down Ctrl + ↓
Toggle Comment Alt + M (custom)
Collapse Alt + Q (custom)
Expand Alt + A (custom)
Go To Line Alt + B G (custom) or Ctrl + G (custom)
Cut Alt + X (custom) or Ctrl + X
Copy Alt + C (custom) or Ctrl + C
Paste Alt + V (custom) or Ctrl + V

Show View Alt + B N (custom)
Activate Editor Alt + B M (custom) or F12

=> j(Alt + F)

贴这篇文章的时候还有个小插曲,上下方向键(即↑和↓)不知道怎么用HTML表示,后来用ruby写了段script,生成了HTML的特殊字符(其实应该是UTF-8编码),找啊找,终于找到了:
&#8593;  =>  ↑
&#8595; => ↓

script写得很简单:
for i in (m..n) do
puts "<span>&\##{i};</span>"
end
保存为tm.rb,将m和n换成你要查找的范围,如32..2000或8000..10000,然后在命令行里运行:

ruby tm.rb > temp.html


再用浏览器打开temp.html,你就会看到很多神奇的符号了。哈哈。

XHTML规则

  1. 网页从一个文档类型声明开始。
  2. 标签和标签属性必须用小写字母。
  3. 双引号对于标签属性是必需的。
  4. 所有标签都必须被关闭。

正则表达式,尽量匹配短的部分

*、+等后面加上?,就表示尽量匹配短的部分,比如:
%(<span class="normal"> </span><span class="keyword">def</span>).sub!(/(<span class="normal">)(.*)(<\/span>)/) {|s| puts s}
=> <span class="normal"> </span><span class="keyword">def</span>

%(<span class="normal"> </span><span class="keyword">def</span>).sub!(/(<span class="normal">)(.*?)(<\/span>)/) {|s| puts s}
=> <span class="normal"> </span>

Ruby参考手册

原著还是Matz
http://www.kuqin.com/rubycndocument/man/index.html

特别是正则表达式的参考,经常要用,备忘:
http://www.kuqin.com/rubycndocument/man/lanstyle_seiki.html

2008年1月11日星期五

Wonderful Cheat Sheets

看《CSS实战手册》后面的资源介绍的时候,发现一个好东西,Web开发的cheat sheet在线大全:
http://www.ilovejackdaniels.com/cheat-sheets/
资料非常丰富,涵盖了HTML,CSS,JS,MySQL,RGB等多种技术的cheat sheet,强烈推荐,绝对是很方便的参考资料。

HTML: http://www.ilovejackdaniels.com/cheat-sheets/html-cheat-sheet/
Regular Expressions: http://www.ilovejackdaniels.com/cheat-sheets/regular-expressions-cheat-sheet/
Ruby on Rails: http://www.ilovejackdaniels.com/cheat-sheets/ruby-on-rails-cheat-sheet/
HTML Character Entities: http://www.ilovejackdaniels.com/cheat-sheets/html-character-entities-cheat-sheet/
JavaScript: http://www.ilovejackdaniels.com/cheat-sheets/javascript-cheat-sheet/
MySQL: http://www.ilovejackdaniels.com/cheat-sheets/mysql-cheat-sheet/
CSS: http://www.ilovejackdaniels.com/cheat-sheets/css-cheat-sheet/
RGB Hex colour chart: http://www.ilovejackdaniels.com/cheat-sheets/colour-chart/

这么全,怎么给这篇文章定tag呢?我是看CSS相关的时候发现这个资源的,就定为CSS,HTML和JS吧,毕竟以Web开发技术为主。

Pidgin保存聊天记录

Ubuntu下自带的一个聊天工具:Pidgin Internet Messenger, 可以集成很多IM,就是以前不知道怎么保存聊天记录。

现在找到方法了:Tools -> Preferences -> Loggin -> Log all instant messages

BTW,以前可以用这个上QQ的,现在上不了了,谁知道解决方法,请告诉我,先谢谢了!

QRe 正则表达式测试工具

主页:http://code.google.com/p/qre/
看样子是中国人开发的。同时提供了Windows下和linux下的jar。

在Ubuntu下打开jar文件的方法:
选中文件,点右键,"Open with Sun Java 6 Runtime"

看样子还挺不错的,正则表达式本来就不好写,也不好测,有个小工具挺能提高效率的。
对正则表达式不熟悉的人(比如我),也可以用来学习。就相当于正则表达式界的irb了,哈哈。

要经常review文章

有时候写Blog写得比较匆忙,或者写得太有兴致了,敲得太快,思维速度超过了手的速度,就会出现一些小的bug。review可以很容易地发现这些bug。

而且写的时候是在编辑页面,写完了后预览或者保存后浏览,可以看到页面究竟是不是按照自己的样式去布局,去显示样式。其实就是站在一个访问者的角度来看,看看顺不顺眼。
其实就是站在用户的角度来考虑,重视用户体验。和Web开发是相通的。

2008年1月10日星期四

Code Highlight

Google的Blog确实给用户很大的灵活性,什么都可以自定义,模板的源代码都可以自己修改。

写技术Blog,贴代码的机会很多,代码的语法高亮就很重要。一开始想雄心勃勃地自己写一个,后来一是没时间,二是写得不一定会比已有的东西好。

Google了一下,语法高亮可以有好几种解决方法。一种是js来控制,这个需要在页面上引人js文件,比较适合自己的空间架的Blog。一种是用CSS来控制字符的色彩,HTML则用工具来生成,我一开始就是想自己写一个这样的工具。

后来终于找到了一个linux的开源项目source highlight,详见:
http://www.gnu.org/software/src-highlite/http://www.gnu.org/software/src-highlite/source-highlight.html

用apt-get的话无法安装最新版的,最好是去下源代码来自己编译:
ftp://ftp.gnu.org/gnu/src-highlite/去下载最新的源代码tar.gz包。解压并到解压后的目录,依次执行:

./configure

make

sudo make install

如果不能编译的话,就要先安装编译环境:

sudo apt-get install build-essential

和boost regex库

sudo apt-get install libboost-regex*


用倒是挺简单,命令行就行,具体参考它的手册。但是默认生成的色彩并不理想,不是TextMate的那种漂亮的颜色,而且高亮的也不是完全正确。比如symbol,应该是:sym这种,而它默认的却是+-=这些操作符。

幸亏source highlight提供很方便的扩展,不用改它本身的代码或配置,可以直接通过外部文件来设置格式。它本身的配置文件是xml格式的,在我Ubuntu 7.10 上的路径是:/usr/share/gtksourceview-2.0
在文件夹language-specs下是语言的定义,xx.lang,比如ruby.lang。
在文件夹styles下是高亮色彩的定义。

source highlight给我们提供了更简单的语法来定义外部的配置文件,不过参考它本身的xml格式的配置文件可以帮助我们配置一些比较复杂的规则。

历经辛苦,勉强定义了一个支持ruby大部分语法规则的.lang配置文件,里面几乎全是用正则表达式。source highlight用的是Boost regex library,而这个库又用的是Perl的正则表达式语法,很不习惯。具体配置如下:

keyword = "__FILE__|and|def|end|in|or|self|unless",
"__LINE__|begin|defined?|ensure|module|redo|super|until",
"BEGIN|break|do|false|next|rescue|then|when",
"END|case|else|for|nil|retry|true|while",
"alias|class|elsif|if|not|return|undef|yield"
keyword = "defined?"

comment start "#"

symbol = ':[a-zA-Z0-9_]+'

variable = '\$[a-zA-Z_][a-zA-Z0-9_]*',
'@@[a-zA-Z_][a-zA-Z0-9_]*',
'@[a-zA-Z_][a-zA-Z0-9_]*'

number = '(?<![\w\.])([1-9](_?[0-9])*|0)(?![\w\.])',
number = '(?<![\w\.])([1-9](_?[0-9])*|0)(?![\w\.])',
'(?<![\w\.])(( (\d(_?\d)*)?\.\d(_?\d)* | \d(_?\d)*\. ) | ( (\d(_?\d)*|(\d(_?\d)*)?\.\d(_?\d)*|\d(_?\d)*\.)[eE][+-]?\d(_?\d)* ))(?![\w\.])',
'(?<![\w\.])0[0-7](_?[0-7])*(?![\w\.])',
'(?<![\w\.])0[dD][0-9](_?[0-9])*(?![\w\.])',
'(?<![\w\.])0[xX][0-9A-Fa-f](_?[0-9A-Fa-f])*(?![\w\.])',
'(?<![\w\.])0[bB][01](_?[01])*(?![\w\.])'

regexp delim "/" "/" escape "\\" multiline
regexp delim "%r{" "}" escape "\\" multiline

string delim "'" "'" escape "\\" multiline
string delim "\"" "\"" escape "\\" multiline
string delim "%{" "}" escape "\\" multiline
string delim "%w(" ")" escape "\\" multiline


我把它保存为rb.lang,放在ruby源代码文件同一目录下。NND,写到后面才发现有个Tutorials on Language Definitions,就是那个手册后面:http://www.gnu.org/software/src-highlite/source-highlight.html#Tutorials-on-Language-Definitions,拜托啊,写手册请把tutorials写在最前面,新手从上往下看,看到后面都成高手了,谁还需要那个速成的呀。

然后就是色彩方案的定义,这个就更简单了,可以直接定义CSS文件,下面是我定义的一个:

body { background-color: black; color: white; }

.keyword { color: #DE9309; font-weight: bold; }
.string { color: #00FF00; font-family: monospace; }
.regexp { color: #0000FF; }
.comment { color: #AB50D6; font-style: italic; }
.number { color: #0080FF; }
.symbol { color: #3F92B7; }
.variable { color: #FF0000; }

我把它保存为ruby_highlight.css,放在rb.lang同一目录下。

现在就可以运行了,在命令行下敲入:

source-highlight -s ruby -f html -i demo.rb -o demo.html --css ruby_highlight.css --lang-def rb.lang

(天啊,source-highlight,16个字符的命令,真是少见地长。)
demo.rb是我要高亮的ruby源代码文件,demo.html是我要输出的html文件名,二者完全可以不同名。
生成后的HTML文件,用浏览器打开,哈哈,不是我显摆,真的非常漂亮,就和TextMate里的一样。

这种方案需要把CSS的内容导入到页面里:
/* for code highlight */
.code_bgcolor {
background-color:#E5E5E5;
}

.keyword { color: #DE9309; font-weight: bold; }
.string { color: #00DF00; font-family: monospace; }
.regexp { color: #0000FF; }
.comment { color: #AB50D6; font-style: italic; }
.number { color: #0080FF; }
.symbol { color: #3F92B7; }
.variable { color: #FF0000; }

/* for command line */
.post .cmd {
background-color:#000000;
padding: 10px;
margin: 7px 0px 7px 0px;
}

.cmd_failed { color:#ff0000; }
.cmd_normal { color:#00ff40; }

/* for quote */
.post .quote {
border: 1px solid #BFBFBF;
margin: 7px 0px;
padding:5px 20px;
}

然后,命令行可以这样写:

<p class="cmd"><span class="cmd_normal">Oh_Yes</span></p>
<p class="cmd"><span class="cmd_failed">Oh_No</span></p>

效果:

Oh_Yes

Oh_No


代码高亮可以这样写:

<pre class="quote code_bgcolor"><tt>
<span class="keyword">def<span>
</tt></pre>

效果:
def

普通引用可以这样写:

<p class="quote">some_text</p>

效果:

some_text


source highlight生成的有讨厌的<span class="normal">,我写了个脚本,可以把它去掉,顺便还加上了pre的class:
`source-highlight -s ruby -f html -i r.rb -o r.html --css ruby_highlight.css --lang-def rb.lang`

sleep 1

code_start = '<pre class="quote code_bgcolor">'

File.open("r.html").each do |line|
line.gsub!(/<pre>/) {|s| s = code_start if s}
line.gsub!(/(<span class="normal">)(.*?)(<\/span>)/) {$2}
puts line
end

然后直接在命令行里输入下面的命令:

ruby blog_scripts.rb > r2.html

哈哈,去r2.html把相应的HTML拷过来就可以了

还有另一种方案,生成的HTML里直接嵌入CSS。先定义一个rb.style(这个语法限制比CSS多,所以有些效果比如背景黑色做不出来,我通过apt-get装的是source highlight 2.4,手册上说的是2.6以后才支持背景色,我晕),放在rb.lang同一目录下。
命令行:

source-highlight -s ruby -f html -i demo.rb -o demo.html --style-file rb.style --lang-def rb.lang

这次生成的HTML就在标签的style属性里直接写CSS了。在网页设计上这是一种很糟糕的方式,很不DRY(Don't Repeat Yourself),而且用了很多已经不推荐使用的font标签,整个HTML的也变大了,膨胀了。

最后居然不知道这篇文章该用什么tag,勉强和编辑本Blog搭边,毕竟代码高亮也是为了贴的代码好看。

2008年1月9日星期三

An Introduction to RSpec

找到一篇文章,叫《RSpec简明指南》:http://blog.csdn.net/xieqibao/archive/2007/10/09/1816839.aspx
原文:http://blog.davidchelimsky.net/articles/2007/05/14/an-introduction-to-rspec-part-i

我在他的Blog里找了半天,也没找到传说中的part II,写技术文章也兴挖坑啊?

文章非常通俗易懂,举的例子也很简单。基本还是他和Dave Astels在RubyConf 2007上现场演示的内容。他哥俩现场表演pair programming,一个描述,一个实现,“很好,很敏捷”。
只是最后我觉得代码有点小问题:
class User
def in_role?(role)
role == "assigned role"
end
def assign_role(role)
end
end
这样居然作者就说一切都搞定了,很明显assign_role方法什么也没做过,而且从这步直接重构就跳到了:
class User
def in_role?(role)
role == @role
end
def assign_role(role)
@role = role
end
end
感觉逻辑上有断层。记忆中他们在RubyConf上演示的demo逻辑很完整,跟这个稍有不同。

于是把视频上的演示代码手工敲了下来:
describe User do
it "should be in a role to which it is assigned" do
dave.assign_role("speaker")
dave.should be_in_role("speaker")
end
end
注意,这里只是描述了dave的某个行为,根本没有管究竟有没有dave这个object,这是后面的事。
然后运行测试,当然会失败,在写实现代码以前,我们应该期望失败,然后实现功能,使其通过。

"undefined local variable or method 'dave' ... "

这里就是BDD的精髓之一了,描述完了后,运行测试,系统自然会告诉你,接下来应该做什么。

系统说需要一个名为dave的局部变量,那我们就照做:
describe User do
it "should be in a role to which it is assigned" do
dave = User.new
dave.assign_role("speaker")
dave.should be_in_role("speaker")
end
end
这次的失败信息不同了:

"undefined method 'assign_role' ... "


那么就来实现这个assign_role方法:
class User
def assign_role
end
end
失败信息:

"wrong number of arguments (1 for 0)"

哈哈,简直“懒”到了极致,你系统说没有方法,那我就给你个空的方法,任何多余的事我都不做。其实是“简”到了极致,跟描述无关的功能,我为什么要去实现呢?

class User
def assign_role(role)
end
end
失败信息:

"undefined method 'in_role?' ... "


class User
def assign_role(role)
end
def in_role?
end
end
失败信息:

"wrong number of arguments (1 for 0)"

这种迭代的步伐也小到了极致。

class User
def assign_role(role)
end
def in_role?(role)
end
end
终于迎来跟描述相关的失败信息了:

"expected in_role?("speaker") to return true, got nil"

dave说通常这时我们会用个非常简单,甚至有点愚蠢的做法来让测试通过。
david在旁边赞同:确实很愚蠢,但这就是我们的做法。

class User
def assign_role(role)
end
def in_role?(role)
true
end
end
终于通过了:

"1 example, 0 failures"

到目前为止,我们都没看过最初定义的行为描述,而是一直照着系统告诉我们的信息在做。系统就像一个不知疲倦的“客户”,当它把需求“背下来”后(我们描述完行为后),它就会不断地告诉你接下来该做什么,以及你刚才做的是否符合它的要求。

既然测试已经通过了,就没有必要再理实现代码了。尽管刚才我们用了个非常蠢的办法来使其通过,但它毕竟通过了。它又蠢又能通过,证明是行为描述得不全面,这是描述的问题,而非实现的问题(因为我们确实已经实现了描述所期望的功能,多余的任何一点事都不该做)。

接下来就是继续描述了,好让刚才那个蠢程序员继续有事可做:
describe User do
it "should be in a role to which it is assigned" do
dave = User.new
dave.assign_role("speaker")
dave.should be_in_role("speaker")
end

it "should not be in a role to which it was not assigned" do
aslak = User.new
aslak.should_not be_in_role("speaker")
end
end
理所当然地又失败了(这里如果没有失败的话,理论上是有问题的,因为客户新提的需求,程序没做任何改动居然就能满足了,要么是以前程序员太多事,自作主张加了些功能,要么是需求提得不对,跟以前的需求重复了):

"expected in_role?("speaker") to return false, got true"

哈哈,报应来了吧,叫你蠢,直接硬编码true,现在客户要false了。

现在就是发挥程序员作用的时候了,来真正意义上实现功能。刚才定义的两个方法,一个是空的,一个直接返回ture,都没做实质性的事。
class User
def assign_role(role)
@role = role
end
def in_role?(role)
@role == role
end
end
又通过了:

"2 examples, 0 failures"

这次我们没有做任何蠢事,我们真正实现了功能,同时满足了那两个描述。

这个demo实在是太棒了!

写Blog也应该敏捷,与主题无关的废话尽量不说,此篇结束。

换模板,调CSS

换了个新的模板,原来那个宽度太窄了,先想试着调宽一些,无奈有些线条图片无法改,只得换了一个以百分比布局的模板。

新模板感觉还行,很简单,没有那些鲜艳花哨的东西。改了一些模板的CSS,包括字体,字体大小,颜色等,感觉看起来更舒服一点。Google的东西就是好,尽量不约束你的行为,几乎支持所有的HTML,CSS,js,完全可以自定义。

You are allowed to put anything you want in your template, since it just becomes an HTML file like any other once it is published. That means that Blogger doesn't specifically disallow any HTML or CSS, but it also means that it's up to you to make sure that what you use is correct and supported by your browser.

经测试,CSS的浏览器兼容性没有问题。Windows XP下:Firefox 2.0, IE 6, IE 7 都没问题。Ubuntu 7.10下:Firefox 2.0, Opera 9.23都没问题,ies4linux少数汉字无法正常显示,但CSS布局一切正常,那是软件本身的问题,不是CSS不兼容。

再废一句话,在linux下谁还用IE啊,除非是开发要调试浏览器兼容性问题。
其实所有的用户都应该抵制IE!!!

总的来说一切都很满意,打算长期在这里住下去了。

Visit blogspot


就是我现在用的这个Blog,登录和编辑是在www.blogger.com上,这个没有问题。但是访问是在xxx.blogspot.com上,从注册的那天开始就一直访问不了,开始还以为是DNS的问题,今天去网上查了一大圈,才发现是“伟大的火墙”干的好事,不是我一个人遇到的问题。

国内的用户可以注册,登录,发布编辑都没问题,就是不能通过xxx.blogspot.com来访问。国外的用户应该都可以访问,但国内大部分都是写的中文,给外国人看有啥用啊?

伟大的狗封网(G*F*W)啊,您不是一个人在战斗,全中国的网民都在跟您战斗啊!

测试blog是否被屏蔽
http://www.websitepulse.com/help/gadgets.china-test.html
专门给中国一个测试页面,真给面子,也说明了只有中国有这种需求。

如果测出来是被屏蔽了,下面的方法可以绕开屏蔽来访问:

  1. 编辑WINDOWS\system32\drivers\etc\hosts 文件(Linux用户是/etc/hosts),然后加入72.14.219.190 xxx.blogspot.com ,其中xxx是你的用户名,即可访问到你自己的BlogSpot了.

  2. http://anonymouse.org/cgi-bin/anon-www.cgi/http:/xxx.blogspot.com, 在xxx处填上你的地址就可以正常访问了.

  3. http://xxx.blogspot.com.nyud.net:8090/

  4. http://www.pkblogs.com/ 或者 http://www.inblogs.net/
(2008年4月初,上面的第4种方法,即pkblogs和inblogs也不能用了,不过1,2,3还继续有效。)

Is your blog blocked in India, Pakistan, Iran or China?
真可悲,一共就四个国家屏蔽,当然朝鲜没有屏蔽,因为他们不能上网。

http://www.pkblogs.com/xxx
http://www.inblogs.net/xxx

如果用上述办法还是不能访问,就要检查blog的状态了:

测试blog能否被访问
http://www.websitepulse.com/help/gadgets.ping-test.html

这篇文章讲得很详细:
http://www.pkblogs.com/betabloggerfordummies.blogspot.com/2007/10/is-china-blocking-your-blog-grrr.html

越南也被光荣地点名了:
Many Asian countries, such as Vietnam, have convinced the ISPs there to block access to BlogSpot.
http://www.pkblogs.com/bloggerstatusforreal.blogspot.com/2007/01/blog-blocked-but-by-who.html

还有个国外的网站,测试网站是否被“伟大的火墙”屏蔽:
www.greatfirewallofchina.org/test/
当然很不幸的是,这个网站本身也被“墙”给屏蔽了,所以国内用不了(用代理也许可以,我没试)。

locate 和 find:两个查找文件的命令(转)

对于初次切换到 Linux 系统的朋友来说,如果想要从命令行界面执行查找文件的任务的话,那么,locate 和 find 是两个需要了解的命令。虽说这两个命令都可以满足查找文件的操作,不过却是各有所长,在选择上不妨兼而用之。

  1. locate:该命令在运行时需要后台索引的数据库作为支撑,在 Ubuntu 中这个数据库文件位于 /var/cache/locate/locatedb。一般来说,这个数据库文件每天是通过 cron 自动更新的。如果不幸没有得到更新,那么可以执行 sudo updatedb 来手动更新。

    假如我想要在系统中查找一个名为 linux.html 的文件,那么可以这样执行命令:locate linux.html。locate 搜索文件的速度很快,一会儿就会把结果列出来。locate 有一个十分有用的选项 -r,它可以让你在搜索文件时使用正则表达式。

  2. find:这是另一个 Linux 系统中重要的文件查找命令。find 命令的功能很强大,其一般使用方法为:find 位置 -name 文件名称。例如,我要在 / 这个根目录中查找 linux.html 文件,可以执行 find / -name linux.html。你除了可以按文件名称来使用 find 查找文件外,也可以根据文件大小(通过 -size n 选项指定)、时间(如 -atime n 表示查找 n 天前访问过的文件)来搜索文件。

    此外,find 命令同样支持在搜索文件时使用正则表达式,你只需指定 -regex 选项即可。

值得注意的是,对于 locate 与 find 这两个命令的解说远非这篇小文所能满足。关于这两个命令的更加详细的用法,你可能需要通过 man locate 或 man find 查询。

原文见:http://hi.baidu.com/nfubuntu/blog/item/b4fd33f359911050342acc70.html

狗儿还是歪

狗儿还是歪
The Doggy Is Awesome

我翻译得好不?
或者语气可以更强烈一点:
The Doggy Is So Fxxking Awesome

从Zed Shaw老兄那里学来的。

死后,你的墓志铭打算写点啥(转)

  1. 感谢政府为我解决了住房问题!
  2. 一居室,求合租,面议。
  3. 小事招魂,大事挖坟。
  4. 发布违规信息,永久封杀! by GCD
  5. 我觉得我还可以抢救一下!
  6. 老子是被活埋的!曰!
  7. 广告位招租
  8. 提供鞭尸服务,一次100!
  9. 初从文,三年不中;后习武,校场发一矢,中鼓吏,逐之出;遂学医,有所成。自撰一良方,服之,卒。
  10. 基因重组中,请稍候……二十年
  11. 我生在中国,我葬在中国,祸不单行啊!
  12. 单挑冥王哈迪斯中,征求组队!(网游篇)
  13. 牧师,帮我复活一下下,谢谢,坐标××.××(网游篇)
  14. 当你看清这行字的时候:朋友,你踩到我了。
  15. 老子终于不用怕鬼了!
  16. 给爷笑一个,要不……爷给你笑一个?
  17. 神农氏的墓志铭:我靠!这草有毒!!!!!
  18. 这是我挖的最后一个坑( 警告挖坑者)
  19. 摸骨算命
  20. 陪聊,提供夜间上门服务

BTW,“转载”这个词用英语该怎么说啊,reproduction?
有知道的请告诉我,先谢谢了!

2008年1月8日星期二

无法忍受的反讽

翻《Programming Ruby》时,无意中发现的注解:

SOAP以前代表Simple Object Access Protocol(简单对象访问协议)。当人们无法忍受这种反讽,这种字母缩写的解释被丢弃了,而SOAP就只是个名字了。
----------------------------
SOAP once stood for Simple Object Access Protocol. When folks could no longer stand the irony, the acronym was dropped, and now SOAP is just a name.


哈哈,看来都对SOAP恨得牙痒啊。REST越来越火了,看SOAP能撑到什么时候。

Lord of War - Zed Shaw

Mongrel的作者发彪了:http://www.zedshaw.com/rants/rails_is_a_ghetto.html

这哥们太牛了,技术很牛,情绪也很牛。还会像Jimi Hendrix一样用左手弹吉他(刚开始看错了,以为他说他能弹得像Jimi Hendrix一样好)
太长了,后来还觉得不尽兴,又写了Round Two,没兴趣没时间没耐性去看完。

javaeye的新闻频道报道了《 Zed Shaw猛烈抨击Rails社区》:http://www.javaeye.com/news/724
chinaonrails上更是直接打出标题《Zed要引发一次核战?》:http://chinaonrails.com/topic/view/1150.html

学习英语: Zed's So Fucking Awesome (ZSFA)

Teaching Rails at a University

RailsConf Europe 2007 上的一篇演讲。

开篇就振聋发聩:If Ruby is so great, why are Universities still teaching Java?

后面先是分析现实状况,谁适合来教(当然是演讲者),大学里的环境适不适合开这门课?似乎是不适合,在德国(演讲者是德国人,看那些ü啊ä啊就猜到了)大 学是做研究的,是科学领域的,培养爱因斯坦的。另一些学校(可能类似于国内的职业技术学院)是学技能(skills)的,修机器的。而学习编程语言是不属于科学领域的。

很遗憾,我承认计算机有一部分领域是需要理论的科学的研究的,但是大部分学生毕业了都是以敲键盘为生。编程是纯粹实践性的,看书听课做题考试这一套用处不大。

后面部分就是比较详细的讨论课程的计划,不但教Ruby和Rails,还教Agile和TDD。前两周还有个魔鬼训练课程(演讲者称其为blackout),"Lock up the students in a computer room for two weeks",每天都教好几样东西,拼命地灌,填,塞。

最后还有总结,看来是已经实践过一次了。哪些地方做得好,学生喜欢(学生们很喜欢那个两周的blackout),哪些地方做得不够,需要改进。

国外在学习推广新技术方面,从起跑线上就已经遥遥领先了。

Behaviour Driven Development Intro

看了Dave Astels写的《BDD Intro》,给了我很多观念上的改变。
  1. focus on testing? 原先的Test Driven Development(TDD),即测试驱动开发,很容易给人们以名称上的误解,以为就是先写测试。而实际上程序员写的测试和测试人员的测试还是有很大 的不同,程序员写测试是为了描述程序要实现的功能,从而来指导自己的设计与实现。所以用behaviour来进行描述,更加自然,更加贴近需求。
  2. 单元测试之误。Unit Test,很容易给人一种感觉,即测试方法与实现方法应该一一对应(1-1 relationship)。而BDD可以一次只描述行为的一个方面(we should be thinking about facets of behaviour),比如在某个上下文环境中,方法应该successful,在另一个上下文环境中,方法又应该failed。描述与实现的方法可以一对多,也就是说BDD的粒度比TDD要小。
  3. specifications VS Verification. 应该是说明(用should),而不是证实(用assert)。“说明”是要描述程序功能应该是什么样的,should_be xxx。“证实”是要确保程序正确运行,这里就有点奇怪,我还没有写程序,怎么知道程序运行正不正确呢?当问题比较简单的时候,开发者一般是没意识到的, 因为下意识里,在写测试的时候,已经在想程序的实现细节了。但是当问题比较复杂的时候,有些开发者就不知道该怎么写测试,或者写出的测试也很复杂,甚至写 出的测试本身都有问题。遇到这种情况,就应该把复杂的问题分解。而BDD就是在引导我们分解问题,通过一个个“在什么什么情况下,程序应该怎么怎么样”, 问题很自然很清晰地描述出来了。原先问题是立体的多面体,现在被展开铺平了。然后再把展开的facets放到一个特制的“点钞机”(autotest) 上,它会告诉你接下来该做什么。
  4. 很小,很精干。在TDD的指导下,通过重构,在大多数情况下我们已经可以让实现方法更短小,更简单,更易理解,但如果方法的分支多一点,异常情况多一 点,对应的测试方法往往会膨胀得“很好,很强大”(可能是功力不够,实现方法也没有重构得很彻底)。BDD本身就让我们每次描述专注在行为的某个方面上, 首先就保证了测试简单容易理解。当然在实际用RSpec的时候,往往受以前写单元测试的惯性思维,总想把功能在一个测试里都描述出来,结果有些测试还是写 得比较臃肿。用的是BDD的形,做的却是旧一套的事。看来工具只能起到有限的约束,只有真正经过实践积累,领悟到了工具的良苦用心,观念上转变了,再强迫 做法的改变,正确地用工具,才能事半功倍。
  5. expected, actual. 我以前在写测试的时候,总有某个时候会忘记,在assertEquals()方法的参数列表里,到底哪个排在前面,是expected还是actual? 虽然写反了并不影响功能,因为等式两边可以交换,等式仍然成立。但当测试出错时,打印的出错信息就会让人迷惑。而RSpec里,根本不用去想到底顺序该如 何,actual.should_equal expected,should后面就该接期望值,很自然的事。因为在使用自然语言时,这样的逻辑已经重复了无数遍,已经深入到了潜意识里,根本不用再思维。
  6. 作者在最后很有意思,既然TDD已经成那样了,人们已经理解错了也用错了,何必再费力气去纠正他们呢,干脆另起炉灶,重新树立一个品牌,就叫BDD。
  7. 最后,其实在项目中真正用的是RSpec + RSpec on Rails,RSpec是针对Ruby的,后者才是针对Rails的扩展。

感觉BDD与TDD并不是矛盾的,并不是说BDD就一定比TDD高级先进。TDD与BDD的目的是一样的,都是用来驱动程序的设计与实现的,用来确保程序 实现了预期的功能,它们提倡的小步迭代,更是在持续地纠正你的方向,让你不至于在错误的道路上越走越远。只是TDD被很多开发者误解,没有领会到它的精 髓,没有正确运用,没完全享受到TDD的好处。BDD进行了一些改进,在观念上强迫你按照behaviour的方式思维,在使用上更加接近于自然语言,让 开发者用编程语言写描述就像是在写一句话一样。
对于真正熟练正确运用的开发者,其实TDD与BDD也就是招式的不同。所谓高手,一草一木皆可为剑。

题外话:原来behaviour和behavior都是正确的拼写,类似于colour与color。

写Blog的原则

  1. 只写与技术相关的,精确一点,只写与Ruby, Rails, Agile相关的。
    (修正一下,还可以写点搞笑的,轻松的)
  2. 拒绝感性的,无病呻吟的,多愁善感的,故作深沉的,发泄的,艺术的,华丽的,空洞的,冗长的;
    提倡理性的,朴素的,简洁的,充实的,专注的,通俗易懂的。
  3. 尽量避免口水话,引子,前言,序,编者按等前戏,最好上来就直奔主题,开门见山。
  4. 经常review写过的文章,发现能够改进的地方要毫不犹豫地refactor。

在这里安家了

一直找不到一个很合适的Blog。

最开始用的是MSN的空间,那玩意用过的都知道,又慢又烂。
QQ空间更加不考虑,连Firefox都不支持。
javaeye的blog其实不错,贴代码也有语法高亮。但那里的风气我不太喜欢,我想找个清净自由点的地方,用以备忘一些技术相关的东东。
前两天还用过myspace,可惜编辑器太次了,对Firefox支持也不够,还是放弃了。
希望Google不要让我失望,当然Google还从来没有让我失望过。

还是有点小问题:
  1. 好像还不支持附件上传,不过这个不常用,不是什么大问题。
  2. xxx.blogspot.com被G*W*F屏蔽了,只能通过www.pkblogs.com来访问。
  3. 字体比较少,没有中文字体。
  4. 没有代码语法高亮,得自己搞定。
总的说来还是很不错,UI和用户体验都很棒。