Ssl Notes With Rails

上周我给小兔盒加上了https,比想象中容易许多。记录一下

免费的CA:

之前一直以为 CA 是需要每年付一笔不菲的费用的,但其实也有免费的,例如: https://www.startssl.com/

按照网站指示,可以获得由其颁发的证书 有两个文件(certification & key) 是后面工作的基础。

在开发环境中使用 https (无需nginx)

1
thin start --ssl --ssl-key-file YOUR_SSL_KEY_FILE --ssl-cert-file YOUR_CERT_FILE

一些 tips

url helpers

不管当前 request 是 https 还是 http,rails 的 url_helper 都使用 http

1
2
3
4
5
6
# file: config/routes:
root to: 'home#index'

# in views or helpers:
root_url                    #=> http://YOUR_SITE/
root_url protocol: 'https'  #=> https://YOUR_SITE/

使用 protocol 无关的 assets 地址

1
2
3
4
5
6
# file: config/environments/production.rb
Rails.application.configure do
  ...
  config.action_controller.asset_host = '//YOUR-CDN.com'
  ...
end

用工具检查

有很多工具可以检查 https 部署情况,例如这个: https://www.sslshopper.com/ssl-checker.html

石潭村游记

石潭村

石潭村在安徽省黄山市徽州区歙县霞坑镇,距离黄山市50多公里,离杭州余杭150公里多点。我们在杭州,所以过去很近,非常适合清明小长假自驾游。从杭州往西,一路有天目山、青山湖这样的景区,即便是在高速公路上,也能看到路边的小风景。

杭徽高速西行大约120km,之后是省道。经过一段蜿蜒刺激的盘山公路,又穿过几个热闹的小镇子,就来到了车子能开到的尽头——石潭村。啰嗦几句,实际上还能往前开,只是政府封了路,里面的路外地车进不去,只能乘坐当地的小商务车,一辆车可以坐八个人。网上也有老的攻略说石潭村里面还有十几个村子,进去需要徒步。其实我们发现已经修了不少水泥路进去,车子可以一直开到5公里深的大龙湾。一路依山傍水,满目都是油菜花,黑砖白墙的村落点缀其间,风景很美。

路边

我们是当天下午到的石潭村,在村口打尖吃饭,人还挺少的。暗自庆幸来对了地方,是个清净的地方。吃完饭再往前走了1公里路,正式进了村子,才发现实际情况稍微糟糕了一点。人虽然没有比肩继踵,但是已经把小村子挤得满满的了,很困难地找了车位,然后随便选了一家名字看上去比较正规的旅店住下,放了行李。

这家旅店的名字有好几部分组成,我们只记得第一部分“游侠客”,后面那部分所有人都没记住,因为看到“游侠客”三个字,有人就兴奋地说认识游侠客的创始人,我们就决定入住这家亲切的旅店了。

有了地方住,心里就踏实了。放下行李,我们一行人就兴致勃勃地出发,往村里头扑去。

石潭村

村子依山傍水而建。山并不高,一条小溪从山间穿过,将石潭村一分为二。溪水不算多,但是很清澈。河滩上有许多青色的鹅卵石。

玩水

我们在小溪边玩了一会,然后就开始随便行走。

村边菜地里面油菜花还开着一些,但是也有许多已经谢了,结了果实。还有葱,一些蚕豆,长得都很粗壮。村子里面很安静。房子都很老,但历史并不长。清一色的白墙,绘了深色的花边,古色古香。有几间木结构的老屋子,看上去很精致。

村子

村子

村子

村子

走着走着,就出了村子,来到了后山。看上去有路往里面走,据说是到另外的村子去的。这时候太阳已经快下山了,我们就决定不继续往前了。但是往回走又心有不甘,才没出来多久呢!边上的小山上,菜地里面中了很多油菜花,好像可以上去的样子,可是又不见有路。这时鸽子mm 咻咻地爬了上去,大家也跟着,顺着隐隐约约的小坎,爬了上去。

上去之后,穿过齐头高的油菜田,来到另一边山头,美景就这么突然映入眼帘。山下的村子,小溪,对面的林子、夕阳,和清新的空气一起,像画一样。

山坡

夕阳

放下野餐毯,大家坐在一起,吃吃喝喝,好不快活!

慢慢地太阳从对面的山头下去了。我们也下山,继续沿着小溪散步。然后就回到旅店稍作休息,便开始找吃晚饭的地方。

晚饭在另外一家本地的食宿一体的旅店吃的。菜很好吃,加上大家有点累,都觉得很美味。回到杭州后鸽子还认真地在大众点评上给了4星好评,这是后话了。

吃完饭已经不早,大家稍微讨论了一下第二天的行程,小朋友不喜欢坐船,所以坐船的方案就被否定了。但去哪里还是没什么定论。累了一天,大家纷纷洗澡睡了。

大龙湾

天刚蒙蒙亮,村子就已经苏醒了。出门务农的人,开张的早餐店,准备上山的摄友……外面逐渐嘈杂起来。

我6点时候醒了,躺在床上看了一下地图,觉得可以沿着小溪往下游走。看到小溪在一个叫做“大龙湾”的地方拐了大弯,有山有水有桥,觉得可以去看看。

其他同伴们还在睡觉中,我决定独自起来看看。

吃过白粥配咸菜的早餐,买了根刚炸出来的油条,在村外随便散步起来了。

早晨的景色果然更美。山中雾气缭绕,空气有点清凉,提神。远处的树林是一层层的,像水彩画一般。透过片片的油菜花,此时的村子最像水墨画了。

早上出门没有带相机,只有一个效果很差、不能自动对焦的手机。。。你们勉强感受一下好了。。

早晨

早晨

早晨

早晨

早晨

信步所至,都是美景。去哪应该都不错。

散步回来,大家已经陆续起床了。然后一起退房、出门,吃早饭,散步。简单商量了一下,就拦了一辆车往大龙湾去了。

沿途都是都是油菜花、小桥流水人家,大家都啧啧赞叹。

路上车、人都很少。开车的师傅说,我们是他见过的第一批去大龙湾的游客,上车的时候,他还以为我们是家在那边的呢!

大约10分钟不到,车子就飞驰到了目的地。

下车的地方,是村子外的桥头。清澈的山泉水沿着村民搭的竹筒子流在路上,又流进小溪。桥下的溪水很清,很多小鱼在不停嬉戏。

大龙湾

穿过桥就到了村子里面。

村子里静悄悄的,偶尔才看到骑自行车的小朋友从我们身边路过。后面还跟着一只小黄狗,好奇地嗅嗅我们,然后迅速追向主人了。

村里人少,但是很热情。我们在一位大爷家门口的长石板凳上坐下休息,和大爷聊天。

吃了点零食,稍微填了一点肚子。然后继续探索。

水边有一颗上了年纪的樟树,落下的叶子盖满了竹林间的小路。

樟树竹林

中饭本来想在水边烧烤的,但是走的时候没有带吃的东西,于是问一位采茶归来的老奶奶买了一些粽子、年糕,准备烧烤。不过老奶奶听说我们要生火,很是担心安全。。最后我们在老奶奶家借了灶,生火做饭。菜都是问老奶奶买的。

人们开始大秀厨艺。。

做中饭

吃了一顿饱饱的中饭,时间也是下午两点了,刚好适合回杭州。打电话叫上午送我们过来的师傅来接我们回去,不过他有事,另外找了一个师傅来接我们。

这是我们在等车:

等车

回到石潭村,我们就心满意足地开车回杭州啦!

Chinese_number: 一个解析汉字数字的 ~rubygem

ruby-china 上有人问到怎么优雅地解析汉字数字,比如 二十五 解析成 25 。我第一反应是应该查一下有没有这样的 gem,因为是一个很普遍的需求,说不定已经有人实现了呢!

不过很可惜,不知道是不是我没有查到,还是真的没有,总之没有找到。所以我就写了一个这样的 gem — chinese_number

用法很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'chinese_number'

ChineseNumber.trans '今天二十万'
#=> "今天200000"

ChineseNumber.find "一年有十二个月三百六十五天"
#=> [{"一"=>1}, {"十二"=>12}, {"三百六十五"=>365}]

ChineseNumber.extract "今天二十晚"
#=> [20]

ChineseNumber::Parser.new.parse '二零一四'
#=> 2014

ChineseNumber::Parser.new.parse '一万三千'
#=> 13000

《代码的未来》 阅读笔记(1)—— JavaScript/CoffeScript/ruby 局部变量处理的异同

…… 身为 JavaScript 程序员的好友说,对 javascript 的不满之一,就是它的变量声明和作用域。 —— Mats

JavaScript

js 可以通过有无var来选择是否创建一个新的作用域:

1
2
3
4
5
6
7
var a = "out";
(function() {
  var a = "in";
})();

alert( a );
// "out"
1
2
3
4
5
6
7
a = "out";
(function() {
  a = "in";
})();

alert( a );
// "in"

很灵活。不幸的是,不小心漏写最外层 var 之后,变量有可能会变成全局变量,这是容易滋生 bug 的地方。

CoffeeScript

CoffeeScript 中统一去掉了var,全部按局部变量处理,和 js 不同的是,不再创建内层作用域了。

1
2
3
4
5
6
7
a = "out"
(() ->
  a = "in"
)()
alert( a )
// "in"
~~~

Ruby

1
2
3
4
5
6
7
8
# version 1
a = "out"
(lambda do
  a = "in"
end).call
puts a
#=> "in"
~~~
1
2
3
4
5
6
7
8
# version 2
# ruby 1.9+
a = "out"
1..2.each do |i; a|
  a = "in"
end
puts a
#=> "out"

Ruby 中默认和 CoffeeScript 一致,使用外层作用域。但多了一种“作用域声明”,允许人工制定创建一个作用域。

结论

语言 同名局部变量处理策略
js 内部优先,可以通过去掉 var 向上查找
coffeescript 外部
ruby 外部优先,可以通过声明强制使用内部

折纸折叠菜单效果

这个效果原先是给团队内部的一个协作网站开发的

暂时还没有时间详细写过程,有兴趣的可以看看代码,交流一下

利用 Grunt (几乎)无痛地做前端开发 (一)

前言

如果你想开发一个js应用,甭管多简单,都要先创建整个宇宙

来看看我们的Javascript小宇宙:

  1. 确定如何根据需求、功能划分模块,如何将代码分成多个文件开发,合成一个发布
  2. 保证上一条的同时,使用 Coffeescript、SCSS/LESS 等技术
  3. 保证上2条的同时,想想怎么在浏览器的刷新后一切都最新
  4. 保证上3条的同时,想想怎么在开发、测试、生产、预发布环境中都OK
  5. 保证上4条的同时,进行TDD式的开发
  6. …这还是js, 我们还没有涉及到HTML

Grunt 可以将创建小宇宙的工作变得轻松很多。

初识 grunt

以一个jQuery插件的开发为例。 开始之前,让我们先安装Grunt.

首先需要Nodejs环境,这里假设你已经安装好了NodejsNPM.

然后安装 grunt :

npm install grunt

命令行方式更适合前端开发。这里我都用命令行来进行操作,windows用户推荐用git-shell或者powershell.

第一招:快速搭建脚手架

Grunt 已经安装好了,第一步就是利用grunt快速搭建脚手架出来。所谓的脚手架,就是指包含了目录结构和初始的一些功能,测试文件的一个环境。我们来搭建一个jquery插件的脚手架:

grunt init:jquery

这时grunt会问你一些问题,比如项目名称, git地址,作者等等,比如:

initing

可以看到,grunt已经帮我们创建了一些经常出现的文件(README, LICENSE等), 单元测试也准备好了。

有些问题可以配置一下默认回答,这样就不需要每次都重新输入了,只要敲回车就行。

第二招:TDD(测试驱动开发) 和 单元测试

现在我们就可以着手写grunt-demo-1这个插件了。假设我们这个插件的目的是让页面上*.alibaba.com域名下的a链接都带上一个图标。那么可以写一些简单的qunit用例。

首先在test/grunt-demo-1.html准备好HTML用例:

1
2
3
4
5
6
7
8
9
...
<div id="qunit-fixture">
    <a href="#functional-link">#nogo</a>
    <a href="http://alibaba.com">grunt</a>
    <a href="http://china.alibaba.com">grunt</a>
    <a href="http://style.china.alibaba.com">grunt</a>
    <a href="http://q.pnq.cc">qhwa</a>
</div>
....

打开上一步grunt已经帮我们生成的test/grunt-demo-1_test.js,写上我们自己的单元测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// file: test/grunt-demo_test.js
module('jQuery#ali-link', {
  setup: function() {
    this.elems = $('#qunit-fixture').children();
  }
});

test('is chainable', 1, function() {
  strictEqual(this.elems.alilink(), this.elems, 'should be chaninable');
});

test('can select', function() {
  equal( $('a:alilink', $('#qunit-fixture')).length, 3,
    'should select links with alibaba domain'
  );
});

test('add icon to alibaba links', function() {
  this.elems.alilink();

  this.elems.each(function(){
    var self = $(this);
    if( /^http:\/\/((.+)\.)?alibaba\.com/.test(this.href) ) {
      ok(self.hasClass('ali-link'));
    } else {
      ok(!self.hasClass('ali-link'));
    }
  });
});
...

然后在命令行运行一下grunt qunit 运行结果是这样的:

grunt qunit first run

各种报错,对吧?不要紧,因为我们还没有开始写代码。接下来我们的目标就是写功能代码,让这个测试通过。

经过一番敲击键盘,以及运行了几次grunt qunit,终于,激动人心时刻到来了,我们通过了所有的单元测试:

grunt qunit pass

最终的功能js如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// file: src/grunt-demo-1.js

(function($) {

  var ALI_LINK_REG = /^http:\/\/(.+\.)?alibaba\.com/;

  // Collection method.
  $.fn.alilink = function() {
    return this.each(function() {
      var self = $(this);
      if( isAliLink(this) ){
        self.addClass('ali-link');
      } else {
        self.removeClass('ali-link');
      }
    });
  };

  // Custom selector.
  $.expr[':'].alilink = function(elem) {
    return isAliLink(elem);
  };

  function isAliLink(el) {
    return /A/i.test(el.tagName) && ALI_LINK_REG.test(el.href);
  }

}(jQuery));

想用jasmine? 当然可以了! 社区有 grunt-jasmine-runner

功能已经完成了,但是插件就完成了吗?非也!

有了单元测试,我们可以放心重构我们的代码,达到最好的可维护性。先让机器帮我们做个code review——也就是linting。

准备好了,就来看下一招:

第三招:利用 lint 提高代码质量

运行命令 grunt lint ,果然有报警:

grunt lint fail

嗯,我们的正则表达式写法不太好,那就来优化一下吧!

又经过一阵修改,我们的代码终于在 grunt lint 时没有报警了,不错!

grunt lint pass

运行一下 grunt qunit ,看到单元测试还能通过,功能正常,不错!

第四招:利用 watch 自动化

上面我们在重构和优化的时候,每次修改了功能js都要运行一遍grunt qunitgrunt lint,grunt 可以帮我们做到一旦有修改,自动运行这两个命令,方便吧?

很简单,运行grunt watch 就好了。然后试一下修改 src/ 下面的 js,就不需要手动去运行qunit和lint任务了。 watch 就是这样的一个任务,监测一些文件,都有更新的时候,自动运行需要的任务。

grunt watch

grunt 还自带了一些其他的任务,比如

  • 用来压缩js和css的min 任务
  • 用来开启一个本地http服务器的 server 任务
  • 用来将几个文件合并成一个的 concat 任务

这些都是我们经常使用的任务。

小结

本文的示例代码已经放到 github 上

这一篇我们认识了 grunt ,下一篇将会看到 grunt 这种以任务为中心的设计,带来的强大扩展性。如果上面的例子已经让你感受到了无痛开发的乐趣,那么那些社区插件保证会让你更过瘾的!敬请期待本系列的第二篇—— 让 grunt 飞起来!

参考

grunt init

Ruby 如何“反转义”字符串

我们知道Ruby中转义字符串可以用inspect或者dump可以将字符串转义:

1
2
"\\t".dump       #=> "\\"\\\\t\\""
"中文".dump     #=> "\\"\\\\u{4e2d}\\\\u{6587}\\""

但有时候我们想把已经被转义的字符串反转义回正常的字符串,怎么办?

其实方法很简单:

1
2
3
4
5
def unescape( src )
  String.class_eval(%Q("#{src}"))
end

p unescape("\\\\t\\\\n") == "\\t\\n"  #=> true

使用Apache Benmark(ab) 对一个 Ruby Web 程序做性能测试

最近要在一些内部系统中嵌入fdlint的功能,检查HTML的语法是否正确。涉及到了系统性能测试的问题,学习了一下,在这里记录一下。

首先是工具的选择,apache自带的性能测试工具ab简单方便,就用他了!

首页(静态页面)的测试

很简单的一个命令搞定:

1
ab -n <总数> -c <并发上限> http://fdlint.mysite.com/

注意最后的斜杠(“/”)没有的话命令会报错。-__–

功能页面(动态)的测试

这个页面需要接受一个form表单(POST形式). 这时可以用ab的参数q指定一个POST数据的内容。

第一步先准备原始数据素材。我这需要提交一段HTML代码,所以找了一个HTML页面,临时下载一个也是很方便的:

1
wget http://china.alibaba.com/ -O fixture.html

第二步是把这个素材变成ab需要的方式,一是做URL编码,二是写成表单的形式。

我写了一个ruby脚本来做编码的工作:

1
2
3
4
5
6
7
8
require 'uri'

str = IO.read('fixture.html')
output = URI.escape(str.gsub(/\\n|\\r/m, ''))

File.open('post.txt', 'w') do |file|
  file << %Q(data=#{output}&type=html)
end

这样就在当前目录下生成了一个post.txt文件,格式是

data=<编码过的HTML代码>&type=html

这是我的程序需要的数据格式,你可以根据实际需要调整。

然后就用ab命令了,我也写了一个shell脚本,减少反复修改执行的重复劳动:

1
2
3
4
5
6
7
8
9
10
# 测试目标url
server=http://fdlint.mysite.com/
amount=5000
samual=500

ab -q \\
   -n $amount -c $samual \\
   -p post.txt \\
   -T application/x-www-form-urlencoded \\
   $server

运行这个脚本,看到如下输出,表示性能测试完成:

> Benchmarking fdlint.mysite.com (be patient).....done

实际执行的时候遇到几个问题。首先是”-T”参数一定要有,而且一定是这个值才可以。官方文档里面只是说如果用了-p来POST, 一定设置T参数指定content-type.

经验总结

  1. 如上面所说,url如果是站点根目录,一定要以斜杠(“/”)结尾
  2. 使用-p之后一定要设置-T参数
  3. 如何确定post过去之后,服务器的返回是正常的?

    1. 可以用ab的-v参数输出详细的日志,例如:ab -v 4 -n 500 -c 5 $url
    2. 在服务器可以记录日志

参考文档

官方文档链接

Fdlint: 让前端code Review更轻松

fdlint

fdlintbencode 和 我最近做的一个实用项目。她的参考是jslint——一个js代码扫描工具。和jslint不同的是,fdlint还可以扫描css和html代码。

为什么会做fdlint

Code review是一项重要却比较花时间的工作,根据阿里巴巴中文站前端Code Review Check List,约有80项检查项目,要完整对照着检查是比较花时间的。太花时间导致的结果就是code review的频率不高。

因此,我们开发了fdlint,对HTML/CSS/JS代码进行分析,找出工具能判断的、不符合规范的代码。fdlint可以节约我们的时间,帮助 code review 更频繁、更深入。

特点:

  1. 能扫描出不符合规则的常见问题,共49项, 详见扫描规则
  2. 提供多种使用方式,适合不同的应用场景 (命令行、webVIM插件Notepad++插件、Eclipse插件)
  3. 规则定制方便,将用DSL编写的规则放入一个目录,立刻生效。
  4. 可以批量扫描多个文件,支持递归扫描目录
  5. 支持多种输出格式

截图

  • 命令行运行

  • web版本

  • Eclipse插件

  • Notepad++插件

  • VIM插件

Say Hello to My New Mate

没错,这是我的新伙伴,今天正式开始我的骑行了,2012的一大目标就是锻炼身体!

bike bike

今天在网上看中的,下午联系,晚上就提车了,真是爽快! 因为信任骑行的人,我对车主“死魂灵”是完完全全的信任,虽然我还是车盲一个,哈哈! 提车的时候,死魂灵无微不至的交代,各种经验之谈浓缩在短短的十分钟之内,从车的调节,到刹车的时候,还有分别时候的关于安全的交代。。我囫囵吞枣般地都短暂记住了,那个场景让我想起了小说里的场面:一个武功高手在传内力给一个战斗力只有5的小朋友。。