持续部署其实并不难,只要在持续构建的单元测试通过后,可以自动将最新的代码部署到测试环境,并重启测试环境的服务,我们就可以在测试服务器上访问到最新的内容了。
安装gitlab很简单,只要按照官方文档一步一步做就好了,有一点需要注意的就是,官方文档的步骤只适合Ubuntu和Debian系统。
安装gitlab之后,我把gitlab_ci装在了另外一台服务器上,当然也可以装在一起。安装时需要注意第二步,文档中使用RVM方式安装的Ruby,我测试时对后续步骤有影响,就干脆按照GitLab安装指南的做法,直接安装的Ruby。如果装在同一台服务器上,还要注意nginx的端口配置,两个不能都使用80端口。
安装一下nose,一会儿需要执行测试。
1
|
|
系统装后好,先在GitLab上创建一个项目,叫examples,我的服务器IP是192.168.8.202,项目的SSH地址是git@192.168.8.202:yachuan.chen/examples.git。
创建本地仓库:
1 2 3 4 5 6 7 8 |
|
接下来在GitLab CI(以后简称CI)添加一个持续构建的项目,我的地址是http://192.168.8.201,这里会出现一个错误提示“Project path is not a git repository”,持续构建的目录必须是一个Git仓库,其实就是在CI服务器上要有一份Examples的Clone,现在SSH到CI服务器上去创建Project path。 由于CI是使用gitlab_ci用户运行的,所以也是用该用户进行clone。
1 2 3 4 5 |
|
直接这么做会报错,因为CI服务器还没有权限使用ssh进行clone,需要本地生成一个ssh key。
1 2 3 |
|
在GitLab>Project>Examples>Deploy Keys中,点击Add Delpoy Key按钮,将id_rsa.pub的内容复制到Key中,再起个名字,再执行刚才的Clone命令就之后,可以就完成Add Project的操作了。注意,Scripts是必填项,这里先只填一个/usr/local/bin/nosetests,后续在补充。
进入CI系统的Project: examples → Details页面: 我使用的GitLab是4.0版本,按照提示,将Project URL和Project Token复制到GitLab → Project → Services中的GitLab CI中。 这时,我们进行git push后,将会触发gitlab ci执行scripts中的脚本。
]]>1 2 |
|
重构之前先分析一下页面内容,需要有以下几个元素:
使用继承Backbone.Model创建Task类作为模型,继承Bankbone.View创建TaskCard作为视图。
1 2 3 |
|
利用javascript是动态语言的特性,模型暂时不需要添加属性和方法,现在需要定义TaskCard视图的属性,一个Card是一个li元素,视图渲染时,会使用Model填充内容,填充的内容定义为一个模板(template)。
1 2 3 4 5 6 7 8 9 10 |
|
现在可以使用Task和TaskCard在页面上显示任务卡片了,我们可以动态的创建任务卡片。
1 2 3 4 5 6 7 8 9 10 11 |
|
下一步将任务栏设计为一个视图类,方便进行动态扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
现在页面上只有一个New状态的任务栏了。这里用到了一个模板taskboard-template,是预先在html中定义好的一段html代码,underscore.js使用JSON格式的Object对模板进行渲染,<%%>之间用Object的属性填充。Taskboard的模板如下:
1 2 3 4 |
|
再添加两个任务栏,分别是Progress和Done。
1 2 3 4 5 6 7 8 9 10 11 |
|
现在三个状态栏都是动态创建的,加入任务卡的代码显得很不协调,因为是直接通过jquery将TaskCard放入到状态栏中,现在需要引入Collection类了。
1 2 3 |
|
Collection是一组Model的集合,并提供了add、remove等集合操作的方法,接下来创建一个New状态的TaskList,并且和New状态的任务栏关联起来。修改之前的代码,让Taskboard可以和TaskList绑定,并监听TaskList的add事件,当add事件被触发时,调用Taskboard的addOne方法,创建一个任务卡并放到状态栏中。同时删除创建任务卡的代码,修改之前添加任务卡的逻辑,不需要在最外层创建TaskCard了。
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 31 32 33 |
|
页面布局调整完后加入拖拽效果,先套用之前的代码逻辑,只是将其转移到TaskCard和TaskBoard中。
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 31 32 33 34 35 36 37 |
|
现在页面上可以实现拖拽效果了,但我们观察浏览器的console发现,拖拽之后,任务集合的状态并没有变,只是视图变了,模型并没有变。我们希望拖拽结束后,被拖拽的Task从原来的集合中删除,并加入到拖入栏目关联的集合中。在drop函数中无法直接获取Task对象,可以利用模型的cid是唯一特点,将cid保存在li元素的id属性中,然后在drop时取出id属性,根据这个id遍历所有任务集合,获取Task对象。修改后代码如下:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
现在观察控制台输出,已经达到我们预期的效果了。其实代码还有重构空间,fadeOut行为应该绑定到任务卡上,为TaskCard自定义一个事件叫dropped,from.remove会触发TaskList的remove事件,在响应remove事件时,再触发一次task的dropped事件,就可以将fadout行为和TaskBorad解耦。重构后的最终代码参考:Gist
]]>首先,把页面中引入的javascript和css都改成完整的URL,并加入jqueryui和jquery。
1 2 3 4 |
|
注意js文件放在页面的最后,这样可以使页面加载更快。这里我只引入了一个了bootstrap.js。
1 2 3 |
|
然后删除页面中Container原来的内容,换成我们需要的白板栏,共有三个状态栏:New、Progress和Done。
1 2 3 4 5 6 7 8 9 10 11 |
|
cardbox是自定义的样式表,用来显示一个状态栏位。
1 2 |
|
加上样式后是这样的:
增加两个个卡片,这里使用html中的ul和li元素实现,不过要调整一下样式表,li元素用了样式ui-helper-reset,这是jqueryui支持的,目的是不显示列表前的原点儿。
1 2 3 4 5 6 7 |
|
要增加一些css,控制任务卡片大小、内外边距以及拖拽时的鼠标图标。custom-state-active一会儿会用到,当状态栏允许放入时会变色。:
1 2 3 4 5 6 7 8 |
|
样式加好后,可以写js了,在页面的最后加上javascript块,先允许New中的任务卡片可以拖拽,具体参数参考jqueryui文档:
1 2 3 4 5 6 7 8 9 10 |
|
现在任务卡片可以拖动了,但是还不能放到其他的状态栏目里,还需要设置其他栏目允许放置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
accept参数指定了那些元素可以被放入该栏目,这里允许New状态的任务卡放入。move_item函数负责将拖拽的元素从原栏目放入新栏目,如果新栏目中不存在ul元素,则创建一个,move_item函数在将卡片拖拽到Progress栏目中,释放鼠标时触发调用drop。按照这个逻辑,设置Done状态栏,只允许拖入Progress状态栏中的任务卡。此时拖拽New状态时,将不能放入Done状态栏。 为了可以将人物卡片重新放回New状态,需要设置New状态栏允许放置,并设置Progress状态和Done状态都可以拖入。
1 2 3 4 5 6 7 |
|
完整代码在这里Gist
]]>javascript中创建对象的两种方式,使用{}和new Object方式(objects.js):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
代码中使用了nodejs中的单元测试框架mocha,使用之前需要先用npm安装,安装到全局环境中需要加入参数“-g”:
$ npm install -g mocha
使用require引入mocha:
var assert = require('assert')
在describe的function中编写测试方法,第一个参数是一组测试的名称:
describe('object', function() {
//这里写测试。
});
在it的function中编写一个测试方法,第一个参数是单个测试的名称:
it('{}', function() {
//测试写在这里。
});
运行测试使用mocha,而不是node:
$ mocha objects.js
․․
✔ 2 tests complete (2 ms)
函数function是javascript的内建类型之一,javascript中的作用域分为全局作用域和局部作用域,局部作用域只体现在function中,即变量的作用域在声明变量的function中,未声明的变量默认使用全局作用域。
1 2 3 4 5 6 7 |
|
在Person这个function中,age相当与一个似有变量,他只能在function内部访问到,而name和say_age都可以在外部访问,同时say_age相当于封装了对age的访问,这就是使用function模拟类的定义。使用代码如下:
1 2 3 |
|
执行结果:
$ node functions.js
jobs
45
如果不使用new来创建Person对象,如下代码:
1 2 3 4 |
|
不使用new的话,相当于只是执行了Person函数,由于函数没有返回值,因此iverson变量是undefined,而name和say_age则被赋值在全局作用域上。执行结果如下:
$ node functions.js
undefined
iverson
45
这一节演示的是使用function模拟类,使用function的作用域模拟类的私有属性。
因为function也是对象,所以function也可以被当做参数或返回值处理。被当做参数时,就是callback方式,在函数内部可以执行传入的callback参数(callback.js):
1 2 3 4 5 6 7 8 |
|
$ node callback.js
jobs
下面定义一个普通的类Model(models.js):
1 2 3 4 5 6 7 8 9 10 11 12 |
|
$ node models.js
1:todo1
修改为工厂模式是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
$ node models.js
1:todo1
使用工厂模式时有一个好处,当忘记使用new创建对象的时候,创建的对象行为和使用new时是一样的。但也有一个缺点,看以下代码:
1 2 3 4 |
|
$ node models.js
1:todo1
false
object
使用工厂模式创建的对象,类型不是Model,而是object。当使用工厂模式时,要考虑应用场景是否依赖对象的类型。
继承是面向对象的一个重要特性,使用javascirpt模拟继承的代码(class.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 31 32 33 34 |
|
Class函数相当与定义类的语法,Class中的inner函数相当于类本身的定义,而inner函数中的name属性相当于类的私有属性。类的继承是通过在子类的prototype中增加父类的方法来实现。执行结果如下:
$ node class.js
show jobs
info
save
]]>另外吸引我的一部分是工作中的本性,管理者不可能让员工按照他的本意工作,那些制定的条条框框、各类规章制度,其实都是摆设,管理者真正可以做的,就是创造和维持一个环境,在这个环境下,员工的技能会得到提升,员工的心情会舒畅,在这个过程中所做的一切,就是工作。反过来说,一个人工作中的表现,从中反映出的也是他的本性,如果我们对待工作马虎,那我们本身就是个马虎懒散的人。
旅馆老板的经营理念也很触动我,第一条,顾客并非永远都是正确的,不过,无论其观点是否正确,我们都应该努力满足他们的需求。按照我的脾气,不正确的是绝对不会去做的,这是今后需要改进的地方,发现客户的真实需求和对客户的价值,而不是纠结于知否正确这个结论。第二条,每个员工应该竭尽所能将自己负责的工作做到最好,假如他做不到,也应该按照这一标准来要求自己,直到他能做到为之。如果他不愿意用这样的标准要求自己,那她就应该离开去找一份更适合自己的工作。这是态度的问题,态度决定一切,正确的态度是对一个员工的基本要求,至少我招聘时是这样认为的。第三条,在旅馆里所有我们会做的事情都要经受检测,而检测的标准就是那些我们还不会做的事情。由此暴露出来的矛盾,就是旅馆成长和发展的动力。这个理念能够让我们不断的前进,正像我们现在做的,每个人心中有对自己工作满分的标准,给现在的自己打个分,衡量出差距,然后在工作中不断改进。我觉得老板的比喻很形象,他觉得公司就是一个武馆,员工就是这里的习武之人,而习武之人最大的对手就是自己。作者总结到高效的工作系统是个竞赛系统,一定不要理解错了,竞赛不是员工之间的竞赛,是关于个人行为的竞赛。
]]>转念一想,作者的本意应该不会这么浅薄,他不是想引导读者去学习如何采用特许经营模式去办公司,重点应该是在“模式”上,无论是特许经营模式,还是XX模式,对于创业者来讲,重要的是要找到适合公司发展的模式。麦当劳的成功,是模式的成功,不是因为他的汉堡物美价廉,也不是因为他的薯条比别人更加香脆,而是因为麦当劳的公司运作模式可以保证任何一家店的产品都是一样的,模式是可复制的,并且在这个模式下,公司是高效运作的。难道成功IT公司的模式也是可复制的吗?这里首先要明确模式是什么,他不是具体的规章制度,不是作息时间,不是办公装潢,不是统一着装;模式是一种做事的态度,一种文化氛围,甚至一种价值观,当公司所有员工的模式保持一致时,公司有了适合自己的模式。
模式不是固定不变的,不同行业、不同环境的模式用法也不是固定的,模式是可以自我进化的,只有具备了自我完善的功能,公司才会持续发展,才不会依赖某个具体的管理者,而管理者们所要做的就是,保证模式的正常的运行,能够及时的修正错误。
]]>1 2 |
|
当字典中存在key时,无论value是否为None,setdefault方法都会忽略default值,直接返回value。为了避免这种情况,引入了一段重复代码逻辑,每次取一个key都要这样写:
1 2 |
|
代码可读性下降了,决定采用采用子类化内建类型的方式重构。重构后代码如下:
1 2 3 |
|
重构过程中,先写doctest,alwaysdefaultdict.py文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
这时候执行测试的结果是:
$ nosetests --with-doctest alwaysdefaultdict.py
F
......
Traceback (most recent call last):
......
Failed example:
d.setdefault('key','default')
Expected:
'default'
Got:
''
......
Failed example:
d.get('key')
Expected:
'default'
Got:
''
......
Failed example:
d.setdefault('key','newvalue')
Expected:
'default'
Got:
''
----------------------------------------------------------------------
Ran 1 test in 0.035s
FAILED (failures=1)
然后实现代码逻辑,最后代码如下:
执行doctest的结果:
$ nosetests --with-doctest alwaysdefaultdict.py
.
----------------------------------------------------------------------
Ran 1 test in 0.096s
OK
]]>书中提到想要创业的人必须集三种身份于一身,创业者、管理者和专业人士,我自己的理解,这三种身份并不仅仅是想创业的人才有,而是每个人都有,只不过比例不同罢了。创业者代表理想、管理者代表意志、专业人士代表着现实。这三种身份不仅影响着创业的成功率,对工作角色以及能否在当前角色中高效工作也有着很大的影响。
创业者,我经常会幻想,理想的状态是什么?理想的团队、理想的工作环境、理想的公司、理想的系统等等。有些不切实际,有些触手可得,这些往往都在脑子里昙花一现,极少能被付诸实施,也许是创业者这个身份所获得的权力很低,经常被专业人士和管理者打败。要想做出成绩,要不断增强创业者的实力才行。
管理者,自从带团队以来,管理的职责越来越多,对沟通和计划能力的要求越来越高,在缺少创业者的指导下,管理者也很难做出正确的判断,往往会对专业人士听之任之,这也是导致创业者长期被打压的原因之一。不过管理者有他的强项,看待问题很淡定,会冷静的思考事情的前因后果,不会武断,并且能接受其他人的意见和建议。沟通时会采用平易近人的态度,引导其他团队成员采用更加高效的工作方式,找到问题、分析问题、解决问题,然后总结,避免重复犯错。
专业人士,这是个很棘手的家伙。脾气暴躁,以自我为中心,只要遇到看不顺眼的人和事,就会暴跳如雷,拨开旁人,独自战斗。喜欢与人争论,只是为了争论而已。喜欢深入了解细节,而忽略了大环境和上下文。这个家伙力量强大,估计我70%都被他控制着。当然现在不是:)
要想在当前的工作角色中获得成功,必须要调整这三种身份的力量比例,创业者20%,管理者50%,专业人士30%。需要创业者指明方向,又不至于目标太多;需要管理者落实计划,带领团队稳步前行;专业人士有他的优势,不能彻底打压,需要他对细节的了解,为管理者和创业者提供具体的反馈信息。
]]>It seems your ruby installation is missing psych (for YAML output).
google了一下,需要安装yaml的包,于是下载之:
wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz
安装之后,有重新ruby,但执行gem仍然报同样的错误,又仔细搜索了一下google,找到一篇文章《Install Ruby 1.9.3 with libyaml on CentOS》,发现做法是一样的,只是执行configure时需要指定参数,而我使用的都是默认值,虽然指定的和默认值是一样的。按照文中的做法操作之后,成功了!
$ wget http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz
$ tar xzvf yaml-0.1.4.tar.gz
$ cd yaml-0.1.4
$ ./configure --prefix=/usr/local
$ make
$ make install
$ wget http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p286.tar.gz
$ tar xzvf ruby-1.9.3-p286.tar.gz
$ cd ruby-1.9.3-p286
$ ./configure --prefix=/usr/local --enable-shared --disable-install-doc --with-opt-dir=/usr/local/lib
$ make
$ make install
]]>