2013年结

总的来说,2013年收获颇丰,感谢同事们(特别鸣谢胡凯,张凯峰,以及RCA的各位同事)的帮助,我的第一本技术书籍JavaScript核心概念及实践在今年5月出版了。8月到10月,我和王磊,张久坤两位同事,和ThoughtWorks其他office的同事一起,在印度为来自世界各地的毕业生上了近两个月的课。12月从第一个项目上roll off下来,然后加入自己的第一个咨询项目。 在期间还去西电给学生讲过一次session,接受了CSDN的一次采访,本来打算赶10月份的校园招聘去学校讲点东西,后来由于出差耽搁了。但是感谢王欢给了我一个在郑大晔校上课的机会,我们每周六会对准ThoughtWorker们讲一些软件开发得session(TDD, Refactor, Pair Programming等)。 总之,2013过的充实而紧张,希望来年能更加的充实,也希望可以跟大家分享更多的有意思的session和博客。 在TWU(ThoughtWorks University) 当老师 2013年,最大的收获,应该是自己对于压力的新的认识。有一回和胡凯聊压力这个事儿,我说我自己是认可平时带着些压力去学习、工作的,胡凯的看法则是不应该是“带着些”,而是带着最大的压力。他给我举了个游泳的例子,说你在岸边是学不会的,只有被被人踢到水里,才有可能学会。 我觉得挺有道理,8月去印度参加TWU,当然有很多困难了,比如语言问题本身;和其他国家来的trainer一起交流的问题;给一群来自世界各地的学生用英文上课的问题;我周五从项目上下来,周六飞去印度普内,周日早上到达休息了半天,然后周三就要给其他国家的trainer用英文讲一个关于社会公正的演讲,而且形式限定在pecha kucha上,在这种压力下,我不止一次的后悔为什么把自己逼的这么紧?我的真实想法是把胡凯踢到水里学游泳,我自己回西安的项目继续每天做story。 但是周三过去了,我的演讲虽然也不至于震惊四座,但是不论是英文,还是主题,还是演讲技巧本身,都得到了比较正常的feedback。我存活了下来! 如果说这个小范围的,面向trainer的,带有彩排性质的演讲的压力值为100的话,那么下一周,面对着二十多个来自世界各地的毕业生,给他们讲一个关于如何给别人feedback的session,压力值至少为500+。即使是来自墨尔本、伦敦、巴西的trainer都开始表情凝重的备课,我自己的心情就更不用说了。还有人开导我说,不论你有多紧张,坐在底下的那些家伙都比你紧张100倍。 我清楚的记得,第一天结束后,我们互相击掌,还去酒吧喝了一杯来庆祝,当然,8点就赶紧撤回住处,准备第二天的session了,但是不管怎样,我又一次的存活下来了! 压力,必须压到自己要放弃,要退缩的那种程度,才能收获最多。一咬牙过去了,就海阔天空,进入一个新境界了。很多时候,自己总会给自己找很多借口,然后自己就把自己荒废了。李仲轩老人讲,练拳也一样,有人一听说有捷径,有了贼心,就不会出功夫了。 练拳 我虽然不能算是“自幼喜欢舞刀弄枪”,但是也一直对武术比较感兴趣,只是没有耐心去吃苦练而已。小时候只是跟父亲学过一点小擒拿之类,后来从家中找到一本《少林罗汉十八手》,自学未果,倒是记得一些招式的名字,如巧纫针,披身捶,僧推门,僧伏虎之类。2006年,看到了《逝去的武林》后,对内家拳产生了浓厚的兴趣,但是苦于没有老师,倒是我的兄长在石家庄,近水楼台先得月,找到一位教授他练习形意拳的老师,我跟着站了几天三体式,学了个劈拳和崩拳。 但是我自己也知道,那跟没练是一样的,练外行都骗不了。 2007年暑假,我跟李林京老师在石家庄学过一段时间的意拳。但是开学之后回到昆明,就开始三天打鱼两天撒网,后来干脆练网也撤了,借口是内家拳练习得有老师,不然十分危险,一旦受伤则后患无穷。武林盛传“练拳容易改拳难”,一旦练错,再来纠正就非常困难,不如不练。 毕业之后,就再也没有练过,但是见过高山,也知道其中的一些窍要,就是不困下功夫练,今年年初开始感觉颈椎不舒服,上班也感觉挺累的,就又开始站站桩休息脊椎。结果一周之内,隐疾全消,而且渐渐觉得精神比以前好一些了,然后就一直坚持站桩,技击桩怕练偏,就站平步桩。 截止今天为止,除了7月份去伦敦的飞机上没站以外(下了飞机赶到酒店赶紧补上),每天都在站。夏天有一次和同事分享任忠信先生的形意拳,有人怀疑是假的,我给他们试了下,虽然没有搭手即飞,发人丈外那么神奇,但是吓吓外行的效果已经有了。 明年一定要抽时间去石家庄找李老师好好学习一段时间。练拳不但在身体上可以让人强健,而且在精神上也有很多的好处。 其实传统武术,最重要的是锻炼脊椎,腰杆挺直之后,内力渐生,丹田气满之后自然中气充足,精神也会变好,再加上老是有一种别人打不过你的优势,自然容易产生自信(大不了打一架嘛,咱不怯)。蔡元培当年说过,要文明其精神,野蛮其体魄,就是这个意思。 项目 今年的12月中旬,我从RCA项目上正式Roll off,从2012年4月加入ThoughtWorks之后,就一直在这个项目上。在这个项目上主要关注在软件交付上,和所有的BAU项目一样,在RCA,能学到一个成熟的框架是怎么实实在在运行的,从开发,测试,仿真,生产环境都是通的,如果有心的话,可以接触到自动部署,负载均衡,数据库的主备配置,各级缓存等生产上才会遇到的问题,但是很多时候又会比较无聊,因为可能更多的是在做老代码的维护,bug的修复,新功能的开发可能较少。 RCA总的来说,有经验的人居多,所以交付压力来说要小一些,而且带新人方面也没有太大压力。所以时间长了,大家都会觉得有点疲。我们会定期举行CBS(Come Build Something),或者CSS(Come Share Something)这样的活动来刺激一下。我们团队上一般有8个人,每次CBS都是有3-4个ideas,然后结对去做,虽然不会产出什么划时代/跨世纪的产品,但是每次也都会有一些产出。可惜在坚持方面做的并不好。从项目上Roll off的当天,团队送我了一本书《人与神话》(据说排在程序员谎称读过的经典书籍排行榜的榜首,不过实话实说,当时非常感动,谢谢我们team和我并肩作战近两年的同事+朋友): 从项目上roll off之后,加入了西安本地的一个咨询项目,以前端工程师的身份加入,新项目的人对前端开发的经验相对来说都比较少,但是有很强烈的学习愿望,而且动力也很足。新项目本身做起来也比较有意思,各种前端的比较现代的技术(测试,本地构建,CI),框架(AngularJS, Jasmine)都可以很好的进行实践。 刚开始的时候,也是压力巨大,但是咬牙挺过前三天之后,就容易多了。单元在新的一年,可以和新的团队一起将这个项目做好。 旅游 年初和老婆去了次厦门,纯粹度假的方式,无计划,无目的,在鼓浪屿上住了两天: 7月份,我和学海兄参加了欧洲的AwayDay,本来准备讲一个关于轻量级web应用开发的session,但是后来由于session太多,我的就被cancel了。 在印度正好赶上印度的AwayDay: 在普内周边的一个古堡上:

December 29, 2013 1 min

如何测试 Controller - Angularjs

AngularJS中的一个典型的Controller 在AngularJS中,Controller主要用于hold一些跟view的有关的状态,以及数据模型,比如界面上某些元素是否展示,以及展示那些内容等。通常来说,Controller会依赖与一个Service来提供数据: app.controller('EventController', ['$scope', 'EventService', function($scope, EventService) { EventService.getEvents().then(function(events) { $scope.events = events; }); }]); 而service本身则需要通过向后台服务发送请求来获取数据: app.factory('EventService', ['$http', '$q', function($http, $q) { return { getEvents: function() { var deferred = $q.defer(); $http.get('/events.json').success(function(result) { deferred.resolve(result); }).error(function(result) { deferred.reject(result); }); return deferred.promise; } }; }]); 通常的做法是返回一个promise对象,然后当数据准备完整之后,controller的then会被执行。 那么对于这种情况(在AngularJS中,算是一个非常典型的场景),我们如何进行单元测试呢? 测试依赖与Service的Controller 通常来讲,在单元级别的测试中,我们肯定不希望Service真正的发送请求,这样就变成了集成测试,而且前端的开发完全依赖与后台的开发进度/稳定程度等。 所以我们需要做一个假的Service,这个假的Service仅仅在测试中存在: var app = angular.module('MyApp'); describe("EventController", function() { var scope, q; var controllerFactory; var mockSerivce = {}; var events = ["Event1", "Event2", "Event3"]; beforeEach(function() { module("MyApp"); inject(function($rootScope, $controller, $q) { controllerFactory = $controller; scope = $rootScope....

December 28, 2013 1 min

Ruby里的元编程

一个场景 元编程在所有的Lisp系语言中应该都是一个必备的feature,coommon lisp, scheme等包含该功能自然不在话下,而比较主流的编程语言如JavaScript,python之流,也或多或少的受到了lisp得影响,在面向对象的同时,也嵌入了一些元编程的特性。 而元编程在ruby中,虽然不如在lisp的宏那样灵活/强大,但是对于被“主流”编程语言影响很久的程序员 – 如我,来说,已经非常震撼了。 很多ruby程序员都是通过rails才慢慢接触到ruby本身的,在rails中,ORM是通过强大到无穷大得ActiveRecord来完成的。 一个简单的示例如: class Person < ActiveRecord::Base end 对应的,数据库中有一个Person的表: CREATE TABLE person ( id int(11) NOT NULL auto_increment, name varchar(255), age int, email varchar(255), PRIMARY KEY (id) ); 这样,在使用模型Person的地方,可以很容易的编写这样的代码: juntao = Person.new juntao.name = 'juntao' juntao.age = 28 juntao.email = 'juntao.qiu@gmail.com' juntao.save 也就是说,开发者仅仅需要简单的创建一个与数据库同名的ruby类,然后这个类(Person)只需要继承自ActiveRecord::Base,那么它就自动的获得了很多的功能。这些神奇的功能就是通过ruby的元编程来完成的。 一个ActiveRecord的拙劣模仿 我们在这里将编写一个简单的类InactiveRecord,当有其他类继承自此类时,会完成如ActiveRecord那样的功能,当然第一步我们并没有数据校验之类的功能,只是简单的将数据存储起来即可: 在person.rb文件中 class Person < InactiveRecord::Base end 在address.rb中: class Address < InactiveRecord::Base end 而在使用他们的地方: require './person' require './address' def test juntao = Person....

December 15, 2013 3 min

expect的两个小脚本

一个实际的场景 大部分命令行程序都被设计成了可以被管道连接起来的,这样在命令行里可以很容易的讲很多命令串起来,从而完成极为强大的功能。比如: $ find . -name "*.js" | xargs basename | uniq | wc -l 这个命令会递归的查找当前目录下所有的JavaScript文件,然后用basename去掉可能存在的路径字符串(比如,basename path/to/file会返回file),然后我们使用uniq来保证查找列表中得每个条目都是唯一的,最后用wc -l来统计行数。 但是并非所有场景都不需要人的干预,比如一个vpn链接的建立过程: 选择vpn的Group名称 填写用户名 填写密码 等待连接建立,然后程序退出 像这种需要用户交互操作的程序,是无法通过常规的方式来完成自动化的。如果你经常需要做这样的操作,比如网络环境并不稳定,每天需要连接2-3次,那也是一个非常烦人的事情。 Expect正是为这种场景设计的。 Expect简介 Expect用来自动化这些交互式的命令行程序,比如telnet, ftp等。 Expect脚本非常简单,基本的模式是: 启动一个命令A 当命令的输出中包含字符串X的话 输入Y 用expect的脚本来表示的话,代码如下: #!/usr/bin/expect spawn A expect "X" send "Y" spawn会启动一个进程,expect会负责和这个进程来交互。如果没有匹配到指定的字符串,则在一个超时时间内expect就会退出。当然可以通过set timeout -1来让程序expect永远等待下去。 两个小例子 自动的登录到vpn服务器上 #!/usr/bin/expect set addr "vpn.service.com.au" set user "qiu.juntao" set pass "mypassword" set timeout -1 spawn /opt/cisco/anyconnect/bin/vpn connect $addr expect "Group: " send "1\r" expect "Username: " send "$user\r" expect "Password: " send "$pass\r" expect ">> state: Connected" 自动通过ssh登录到远程 另外一个问题是,如果需要这个session保持下去,比如需要自动ssh到一个远程的服务器上,但是又不想每次都输入认证信息,则可以进入inactive模式:...

December 14, 2013 1 min

依赖管理器Bower简介

Bower简介 Bower安装及简单配置 Bower是一个基于Node.js的依赖管理工具,它是一个npm的包,因此安装十分简单,由于我们需要在所有项目中都可以使用bower,因此将其安装在全局目录下: $ npm install -g bower 安装完成之后,可以通过bower search来搜索需要的包,比如: $ bower search underscore 典型的应用场景可能会是这样的,新建一个项目目录,然后运行bower init: $ mkdir -p listing $ cd listing $ bower init 和Grunt类似,bower会问你一些问题,比如项目名称,项目入口点,作者信息之类: { "name": "listing", "version": "0.0.0", "authors": [ "Qiu Juntao <juntao.qiu@gmail.com>" ], "main": "src/app.js", "license": "MIT", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ] } 比如我们需要安装jQuery和underscore.js,则很简单的运行bower install命令即可: $ bower install jquery $ bower install underscore 如果需要团队中的其他成员可以在本地恢复我们的环境,需要在bower.json中指定dependencies小节: "dependencies": { "jquery": "~2.0.3", "underscore": "~1.5.2" } 所有的JavaScript包都被安装到了本地的bower_components目录下,如果有了bower....

October 9, 2013 1 min