All Posts

[译] TrimPath模板引擎语法

本文翻译自JavaScript Template Syntax 表达式与表达式修饰符 ${expr} ${expr|modifier} ${expr|modifier1|modifier2|...|modifierN} ${expr|modifier1:argExpr1_1} ${expr|modifier1:argExpr1_1,argExpr1_2,...,argExpr1_N} ${expr|modifier1:argExpr1_1,argExpr1_2|...|modifierN:argExprN_1,argExprN_2,...,argExprN_M} expr可以是不包含右花括号}的任何合法JavaScript表达式 修饰符的格式是modifierName[:argExpr1[,argExpr2[,argExprN]]] argExpr可以是任何合法的expr Examples: ${customer.firstName} ${customer.firstName|capitalize} ${customer.firstName|default:"no name"|capitalize} ${article.getCreationDate()|default:new Date()|toCalenderControl:"YYYY.MM.DD",true,"Creation Date"} ${(lastQuarter.calcRevenue() - fixedCosts) / 1000000} 这里有一份内置的修饰符列表,你也可以通过TrimPath提供的API来创建自定义的修饰符。 表达式还可以写成${% customer.firstName %}这种形式,多出的%字符允许你的表达式中出现花括号}。例如: Visit our ${% emitLink('Solutions and Products', { color: 'red', blink: false }) %} 表达式中的空白字符是可选的,你也可以写成下面的格式: ${%customer.firstName%} ${%customer.firstName|capitalize%} 声明 声明标签可以嵌套使用。 控制流 {if testExpr} {elseif testExpr} {else} {/if} testExpr可以是任何不包含}的JavaScript表达式 testExpr不需要被(和)包围 示例:

AngularJS中的依赖注入

本文译自https://docs.angularjs.org/guide/di 依赖注入 依赖注入(DI)是一种解决组件如何获取其依赖这一问题的设计模式。 Angular injector子系统负责创建组件,解决它们的依赖,并按要求将它们提供给其他组件。 如果想深入了解DI,可以查看依赖注入的Wikipedia以及Martin Fowler的这篇Inversion of Control。 DI in a Nutshell 一个组件要想获取它的依赖可以通过以下三种途径: 直接创建这个依赖的一个实例,通常使用new操作符 通过全局变量查找已经被创建的依赖 依赖可以作为参数传递给需要它的组件 前两种获取依赖的方式并不理想,因为这会导致依赖关系被硬编码进代码中。这会导致更新依赖关系变得很复杂。在测试时尤其如此,我们通常会提供mock过的依赖关系以隔离不同组件间的测试,硬编码会导致每次测试都需要修改相关的源码。 相比之下,第三种方式是最可行的,因为它将解决依赖关系的责任从组件中移除。依赖只是简单的作为参数传递给组件: function SomeClass(greeter) { this.greeter = greeter; } SomeClass.prototype.doSomething = function(name) { this.greeter.greet(name); } 在上边的例子中,SomeClass并不关心如何创建或找到greeter,它只是在初始化时从参数列表里取出需要的依赖即可。 但这样做的问题是,寻找/创建依赖的责任被转交给了调用SomeClass的代码。 为了统一管理依赖关系,每个Angular应用都有一个injector。injector是一个服务定位器,它负责查找或创建依赖。 下面是一个使用injector服务的例子: // Provide the wiring information in a module var myModule = angular.module('myModule', []); 下面的代码告诉injector如何创建greeter服务。需要注意的是greeter又依赖于$window服务。greeter实际上是一个包含greet方法的对象。 myModule.factory('greeter', function($window) { return { greet: function(text) { $window.alert(text); } }; }); 下面的代码演示了如何创建一个injector并通过它来请求greeter服务:

AngularJS源码阅读之ngModel

数据的双向绑定可能是Angular最为人们熟知的特性之一。举个最简单的例子 可以在result页面中看到,每当在input中输入时,$scope中对应model的值也改变了。反之,当用户点击Set按钮在$scope中更新了model的值时,input输入框中的内容也对应更新了。 所有的这些魔法只需要我们在input元素上指定一个ng-model属性。可见ng-model这个directive是双向绑定这一特性不可缺少一点。本文就继续从源代码入手看看ngModel的实现方式以及其它directive是如何与ngModel交互的。 ngModel被定义在input.js。从名字上可看出,该文件还定义input这个directive,从这点也可以看出ngModel与input之间的紧密关系。 注: 文中所有涉及到的AngularJS源码均来自angular-1.2.8版本。 var ngModelDirective = function() { return { require: ['ngModel', '^?form'], controller: NgModelController, link: function(scope, element, attr, ctrls) { // notify others, especially parent forms var modelCtrl = ctrls[0], formCtrl = ctrls[1] || nullFormCtrl; formCtrl.$addControl(modelCtrl); scope.$on('$destroy', function() { formCtrl.$removeControl(modelCtrl); }); } }; }; 可以看出ngModel依赖于ngModelController以及可选的formController。ngModel在link方法中只做了2件事: 如果声明了ngModel的元素出现在一个form中,那么就向上层form注册自身 注册了$destroy事件的监听器,该事件触发时告知上层form移除对自身的引用 link方法如此简单以至于我们还没看到关于数据绑定的任何信息。看来问题的答案都藏在ngModelController中了。接下来看看ngModelController中都发生了什么。 var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', function($scope, $exceptionHandler, $attr, $element, $parse) { this.

Angular中的Controller

在Angular中,一个Controller是一个JavaScript构造函数。当一个Controller通过ng-controller附加到某个DOM元素上时,Angular会使用相应Controller的构造函数来初始化一个新的Controller对象。一个新的child scope会被创建并作为可注入的参数(就是我们看到的$scope)传递给该Controller的构造函数。 使用Controller: 为$scope对象设置初始状态 为$scope对象添加行为 不要使用Controller: 修改DOM —— Controllers应该只负责业务逻辑。将任何UI的逻辑放进Controller都会极大地破坏其可测试性。Angular中可以使用数据绑定用来更新页面中的数据或使用directive来封装DOM操作。 格式化input数据 —— 使用angular form controls 过滤输出数据 —— 使用Angular中的filter机制 跨Controller之间的代码或状态共享 —— 使用Angular中的service机制 管理其他组件的生命周期

AngularJS源码阅读之ngRepeat

注: 文中所有涉及到的AngularJS源码均来自angular-1.2.8版本。 注2: 本文结构为一段代码配一段说明,代码在上,说明在下。 从3月3号到现在,进入新公司也有1个月了。过去这一个月里主要负责公司内部某个DevOps工具开发过程中的前端部分。由于涉及到较多的网页交互,该项目的前端部分使用了AngularJS这个框架。本人这正好借助这个机会进一步了解了AngularJS的相关知识。 这两天项目主体完成,准备上线,算是有了一些自由时间,正好借此机会学习一下AngularJS内部的实现机制。 本篇来说说最常见的directive之一 ngRepeat 该文件的整体结构如下: var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var NG_REMOVED = '$$NG_REMOVED'; var ngRepeatMinErr = minErr('ngRepeat'); return { transclude: 'element', priority: 1000, terminal: true, $$tlb: true, link: function($scope, $element, $attr, ctrl, $transclude){ ..... } }; function getBlockStart(block) { return block.clone[0]; } function getBlockEnd(block) { return block.clone[block.clone.length - 1]; } }]; ngRepeatDirective是一个数组,它会被传递给directive方法用来生成ngRepeat这个directive。可以看到ngRepeat依赖于$parse和$animate两个服务。$parse用来将字符串解析成javascript函数。$animate用来给DOM改变附加上动画效果。文件最后声明了两个helper方法,会在稍后的分析中介绍到。 接下来把最大的篇幅交给这个directive的核心——link方法。 var expression = $attr.ngRepeat; var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/), trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, hashFnLocals = {$id: hashKey}; if (!

AngularJS Directive之compile与link

刚开始学习 AngularJS Directive 时,总是不太能搞明白 compile 和 link 两个属性的含义和区别。最近写的多一些,读的资料也多了一些,渐渐有了一些理解,简单记录下来: compile 属性是一个函数。该函数会针对每一个 directive 的实例执行一次,在 compile 阶段: 不能访问 scope 元素还没有被插入 document 可以修改 template,最终结果会被 angular 缓存 link 属性可以有两种配置方式。 link 属性可以被设置为一个对象,包含 pre, post 两个属性,分别对应一个函数(preLink, postLink)。preList,postLink 的区别在于执行顺序。preLink 是自顶向下的,先父节点,后子节点,postLink 正好相反。 .directive{ return { link: { pre: function preLink(){ ... }, post: function postLink() { ... } } } } link 属性也可以被直接设置为一个函数,则默认相当于设置了 postLink 函数。 .directive{ return { link: function link(){ ... } } } 等于

使用CSS3实现逐格动画

首先我们有这样一张照片: 照片长度为1184px,高度为75px,共分为24格。 首先我们将DIV大小设置成单格照片的大小,49x75。这样在初始状态下看到的就是第一格的照片。 接下来定义的是最终状态也就是最后一格的状态,通过偏移量背景调整到最后一格。 最后就是定义动画效果,在下面的设置中时长2秒,动画会无限循环下去。 @keyframes wave { to { background-position: -1184px 0 } } @-webkit-keyframes wave { to { background-position: -1184px 0 } } #hahaha { margin: 50px auto; width: 49px; height: 75px; background: url('https://raw.github.com/loveky/loveky.github.io/master/assets/images/css3-stop-animation-background.png') 0 0; -webkit-animation: wave 2s infinite steps(24); animation: 2s wave infinite steps(24); } 效果如下: 由于照片是自己剪辑拼成的,所以偏移量不是太精确。。。

再出发,写在离开之前

从2011年12月12号到2014年2月28号,不知不觉在上海这座城市度过了798天。 在来eBay之前,我还没用过Git,更没听说过ClearCase 打算离开第一家公司时还没想过今后要做什么,只是厌倦了日复一日没有任何技术含量的工作已经对公司前景非常不看好。当时就是在这样一个迷茫的环境中开始骑驴找马的过程,整个过程并不漫长,只面试里 两家。第一家是苏州一个叫玩啥e族的网站,职位是系统工程师,负责服务器维护,不过在一面之后便杳无音讯。原因大概是因为没啥实际经验。也难怪,在第一家公司虽然也是做系统管理员,但都是客户要做什么就照着文档一步一步敲命令,学到的东西非常有限。第二次面试便是George帮忙联系的上海CSC外派eBay的机会。当时没用过Git,更没听说过ClearCase,并且第一次知道还有SCM这样的工作。不过很巧,eBay当时并没有考虑找一名非常有经验的工程师,而是准备找一个“白板”慢慢培养。也许这也算是缘分,在抱着《Git权威指南》突击了几天后,我居然通过了面试–我当时完全没有抱希望。 海绵吸水般的半年 来到eBay,全新的工作内容,全新的技术,全新的环境。对什么都很新鲜,对什么任务都很热情,每天第一个到,最后一个走。不仅仅是因为需要花时间学习同时完成工作任务,更是因为eBay这样一个大公司有很多东西都很吸引我,吸引我去了解,学习。渐渐的,了解了Git,学习了ClearCase,渐渐的,也能帮Developer解决一些开发过程中遇到的问题,刚刚进入公司时觉得非常“高精尖”的东西也慢慢变得不再神秘。除了纯粹的技术上的进步,我还了解了向eBay这样的大公司,遍布在全球各个site的工程师是如何协同工作;了解了作为一个support engineer如何帮助客户快速解决开发过程中遇到的问题;了解了美国工程师如何思考,解决问题。而最后这一点,也是我认为在这过去的2年多的时间里对我影响最大的。遇到问题时,不应该只是忙于寻求一个解决方案,更重要的是思考问题背后的事情。重要的不是“我能怎样”,而是“我应该怎样”(Do the right thing)。也许这也算是工程师文化的一部分吧。 我“被”离职了 好景不常,来到eBay的半年后,我“被”离职了。起因是新上任的VP推出了一条节省开支的策略,清理掉所有他手下的外派人员。于是我又开始了找工作,由于有在eBay的工作经验,这次工作找起来还算顺利,先后拿到了携程,EMC和一家小公司的职位。工作内容分别是构建一套日志分析系统,做EMC产品的技术支持以及perl脚本开发。虽然找到了工作,但当时其实并不想离开。原因是工作刚刚由熟悉到熟练并且对工作内容又很感兴趣,正要更进一步的学习,此时离开难免有些“不过瘾”。大概是出于对我前半年工作的肯定,老板在最后一刻争取到了一个让我多留在eBay几个月的机会,并最终帮我申请到了专为eBay正式员工的名额。也算是有些“因祸得福”吧。 味道变了 在eBay工作的2年中,每年都有很大的收获。区别在于第一年收获都在工作中,而第二年的收获都在工作之外。在结束了“海绵吸水”似的第一年后,刚刚过去的这一年更多的似乎是各种问题。因为刚到新公司时,所有的精力都在学习和任务上。而等到对工作慢慢熟练之后,精力慢慢转移到对工作本身的思考上来。其实,很多事情是禁不住思考的,每当发现工作中有的事情不是在do the right thing时,我都会如实的讲给manager。所以我一直认为,“唯命是从”的员工不是好员工,因为你没有承担自己思考,反馈的义务。工作并不单单是把事情搞定那么简单。make it work以后更要make it right. Ruby? Rails? 没错,不得不提的主题。过去一年里的业余时间都是围绕他俩展开的。12年年底开始接触Ruby,相较于Perl而言,Ruby更fasion更“性感“更自然。接着我接触到了Sinatra,可以写一些简单的页面。再后来接触到了大名鼎鼎的Rails,并且很快就做出了一个Blog应用。Ruby/Rails的简单,友好深深的吸引着我。接着我开始不满足于自己看书学习了。恰在此时,在RubyChina上看到了xmonkeycn的一个拖延症患者的精益(Lean Startup)实践这篇帖子。于是毫不犹豫的报了茶叶班的中级班,详细的学习了一下Rails,理解了Rails是如何工作的。随后在去年4月份的的RubyTuesday上认识了xmonkeycn。随后帮忙写了一些批量下载照片的脚本 接着便接触到了他的小团队(额外还有1个前端妹子和一个做业务的帅哥)——他们当时正在”密谋“做一些”事情“。就这样,业余时间一边学习茶叶班高级班的课程,一边帮xmonkeycn和他的小伙伴的贡献一些和”低级”的代码。慢慢的,我知道了TDD,开始写测试了,又慢慢的,渐渐的熟悉了CSS和JS。就这样,每天工作上的”抵触情绪“和业余时间学习,coding获得的满足感形成了巨大的反差。我甚至开始幻想做一名全职Ruby/Rails开发人员。一晃大半年过去了,就这样时间到了13年下半年。由于家庭原因,我开始寻找北京的工作机会,在一系列SCM相关职位面试的经历之后,我不得不考虑Ruby这条道路的可能性有多大。于是尝试性的在RubyChina论坛上看起了招聘的信息,并在13年的最后一个月投出了第一份Ruby Engineer的简历。1个月后,我收到了北京一家互联网公司的offer。虽然公司没有eBay大,待遇也没有上海这边好,但在思考过后,还是接受了这个机会。因为life is too short,为何不把时间放在能让自己快乐的事情上呢?何况,只有happy work,才能出彩。趁着年轻,尽管折腾吧! 路在脚下,再出发 还有十来天就要离开这座城市,也许在踏上火车的那一刻会有不舍,也许以后很少再有机会回到这里,但终归这里给我留下了很多美好的回忆,感谢所有的同事,朋友,给我的帮助,鼓励。祝一切都好。 过去的2年多里,我: 感谢我的女朋友,一直没能陪在她身边,有很多亏欠,只希望在以后的日子里慢慢弥补 学会Rails已经一系列相关的技术,并最终转型为一个全职的开发人员 结识了很多好朋友,一直在工作内外给我帮助,不能一一列出姓名,谨祝工作顺利 渐渐的开始锻炼身体,开始跑步 在无人指导的情况下,无师自通,学会了做饭。。。 开始渐渐的学会思考,尝试make it right而不仅仅是make it work 喜欢上了阅读,电子书,纸质书,中文,外文,技术,社科,来者不拒

你可能不知道的git clean

git clean是干啥的? git clean可以帮你清理workspace中未被git版本控制的文件,比如临时文件,构建出来的二进制文件。 使用方法 如果你的clean.requireForce项没有设置为false,那么每次使用git clean时都要加上-f参数 示例如下,workspace中有2个调试logo时创建的临时png文件,commit之前需要删掉。如果不用git clean的话需要一个一个rm。 LM-SHC-00355679@17:42:26:~/Angular/pomodoro-time (master) => git status -s ?? image/logo1.png ?? image/logo2.png 使用git clean可以快速清楚,当临时文件较多时尤其方便。 LM-SHC-00355679@17:42:30:~/Angular/pomodoro-time (master) => git clean -f Removing image/logo1.png Removing image/logo2.png 有的时候可能需要将当前workspace打包成zip。但是直接打包会将.gitignore里的文件也打进去。这是git clean也可以帮忙。只需加上-x参数。 LM-SHC-00355679@17:48:13:~/Angular/pomodoro-time (master) => cat .gitignore /coverage LM-SHC-00355679@17:48:16:~/Angular/pomodoro-time (master) # -d 参数表示连同目录一起删除 => git clean -xfd Removing coverage/ LM-SHC-00355679@17:48:25:~/Angular/pomodoro-time (master) => ls coverage ls: coverage: No such file or directory 以上就是git clean的基本用法啦。下面介绍下git clean的其他可选参数: -n 并不实际执行删除操作,只显示出将被清理的文件列表 -X 仅删除.

搭建属于自己的travis pro

Travis是一个众所周知的持续集成服务提供商,免费为开源项目提供持续集成服务。但是对于闭源项目/private repo需要购买travis pro服务才能使用。对于小团队,每月129刀的最低消费实在是一笔不小的开支,因此便有了本文。 为什么要做这件事? 首先,我们是一群有情(jie)节(cao)的程序猿。我们要对自己的代码负责,保证所有上线的代码都是经过测试的。所有Pull Request的code要经过team member的revew并跑通测试。 其次,我们是一群节(diao)俭(si)的程序猿。Travis Pro每个月129刀的最低消费让我们望(xia)而(niao)却(le)步(!)。 要搭建这样一台持续集成服务器,需要哪些东西? 一台linux主机 (本文中使用的是linode上的Ubuntu 12.04) 一个GitHub账号 (加为你repo的collaborator或是team的member) 如何配置? Step1 安装Jenkins,非Ubuntu系统安装Jenkins请参考Jenkins官方文档 wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add - sudo echo 'deb http://pkg.jenkins-ci.org/debian binary/' >> /etc/apt/sources.list sudo apt-get update sudo apt-get install jenkins Setp2 为jenkins用户安装rvm sudo su - jenkins curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3 Setp3 安装Xvfb。(如果你的测试不需要用模拟的DISPLAY,那你可以忽略此步骤) 我们测试Rails时需要用capybar跑feature spec。有js相关的测试需要启动一个浏览器来执行测试代码,但是linode上的Ubuntu都是没有display的,因此跑feature spec的时候会遇到无法启动浏览器而报错的情况。Xvfb就是用来创建一个虚拟的Display供启动浏览器用的。 sudo apt-get install xvfb Xvfb :99 -screen 0 1024x768x16 >> /tmp/Xvfb.