Grunt serve with Proxy

在用 Grunt 开发时可能需要连接外部服务,比如 grunt serve 服务在 http://127.0.0.1:9000, 当前页需要请求 http://127.0.0.1:9090/api 服务,这时候如果直接请求 /api 就变成了 http://127.0.0.1:9000/api,结果 404,因为这个地址是不存在的;如果直接请求 http://127.0.0.1:9090/api 就会出现跨域问题。

比较方便的解决方案是在 grunt server 层做个代理,把 /api 请求转发到需要的服务。有个现成插件 grunt-connect-proxy 可以直接用。

下载安装:npm install grunt-connect-proxy --save-dev,在 Gruntfile.js 添加 grunt.loadNpmTasks('grunt-connect-proxy'); 启用。修改 connect 段设置,添加 proxies 和 livereload - middleware:

grunt.initConfig({
    connect: {
      options: {
        port: 9000,
        // Change this to '0.0.0.0' to access the server from outside.
        hostname: 'localhost',
        livereload: 35729
      },
      livereload: {
        options: {
          open: true,
          base: [
            '.tmp',
            '<%= yeoman.app %>'
          ],
          middleware: function (connect, options) {
            var middlewares = [];
            var directory = options.directory || options.base[options.base.length - 1];
            if (!Array.isArray(options.base)) {
                options.base = [options.base];
            }
            options.base.forEach(function(base) {
                // Serve static files.
                middlewares.push(connect.static(base));
            });

            // Setup the proxy
            middlewares.push(require('grunt-connect-proxy/lib/utils').proxyRequest);

            // Make directory browse-able.
            middlewares.push(connect.directory(directory));

            return middlewares;
          }
        }
      },
      proxies: [
        {
          context: '/api',
          host: '127.0.0.1',
          port: 9090,
          https: false,
          changeOrigin: false,
          xforward: false
        }
      ]
    },
});

serve 任务下添加 configureProxies,注意要加在 connect 任务前:

grunt.registerTask('serve', function (target) {
  if (target === 'dist') {
    return grunt.task.run(['build', 'connect:dist:keepalive']);
  }

  grunt.task.run([
    'clean:server',
    'bower-install',
    'concurrent:server',
    'autoprefixer',
    'configureProxies',
    'connect:livereload',
    'watch'
  ]);
});

重启 grunt serve 即可。

Date Timestamp Conversion in Lua

Datetime to Timestamp

local dt = {year=2013, month=12, day=25, hour=0, min=0, sec=0}
print(os.time(dt))

-- 1387900800

Timestamp to Datetime

local ts = os.time()
print(os.date('%Y-%m-%d %H:%M:%S', ts)

-- 2013-12-25 22:09:51

More: Date and Time

匆匆

李剑青 -「匆匆」:

离家时故作轻松 留给娘的是匆匆

那些过时的青春梦
普通得不能再普通
你肯定懂
褪尽了青涩和懵懂
当人在异乡才知感动

[self review:2013];

2013 年度个人总结。先对照去年计划。

工作

iDev 深入,比如 runtime,自动化测试等,尝试一下 OS X 开发。

Fail。

服务端开发学习。

Done.

学一门新语言,Lua/Go。

Done.

生活

学车考驾照。

Done.

健身锻炼。

Fail.

60 及格分。


工作

虎头蛇尾。

按照去年的个人工作计划,今年有意加强了服务端开发,加上团队的支持,取得了一点点成绩,但总体来说今年的工作暴露了自己很多的问题。

  1. 产品能力欠缺。随着规模的扩大,团队有意识的让几个老员工尝试开发+产品管理双轨制,在大家都比较顺利的转型成功干的热火朝天的时候,自己这边却一直没啥起色。主要原因是自己对产品热情不够,缺乏长期的对某个产品的深入思考,给人的感觉就是这个东西只了解了表层,对整体发展方向没有深挖扩展,很少有价值的建设性意见。也许是因为一直以来的项目都有具体产品负责人,也许是因为太偏向技术,总之现在自己的产品能力和大家有很大差距,游离在团队核心边缘。

  2. 带团队的能力不足。作为 leader 对项目进度的把控经常出现偏差,缺乏规划、进度检查和反思,没有及时提早发现问题,往往等出现问题已经是很严重了。另外一方面就是对团队氛围建设的忽视,很多时候只关注自己手头的事,没有调动大家对项目的信心和积极性。这个的原因可能和自己做开发出身比较偏向温和、自觉的做事方式有关,忽视了团队的执行力,在协同工作没有起到引导作用。

  3. 开发以外的沟通较少。不管是项目内还是和其他项目组之间关于产品、运营、测试等等方面的沟通交流都不多,这个完全就是自己原因,产品热情不高,一步慢步步慢。

  4. iOS 开发深度挖掘不够。不仅深度挖掘不够,甚至已有技术都有些生疏。下半年 iOS 7 的发布了解关注了大量的新增特性,但只有理论学习,缺乏实际项目实践,完全是纸上谈兵,更别说 runtime 等深入。iOS 开发能力没有提升,这才是自己最大的危机感。

  5. 服务端技术深度不够。目前自己服务端能力只是应用层问题的解决实现,在出现框架或偏底层问题的时候就没了思路,考虑问题不周全,容易跑偏。

  6. Full-Stack 很容易多个都知道,多个都不精。这就是自己目前的技术现状,接下来要做的就是技术的深学习,基础,体系架构。

  7. 执行力不够。以上问题的原因都可以归结于一点,自己的执行力还很不够,看看 iDevNotes 还有几篇笔记未完成,LearnList 那么多没看,包括 HackingWeek 项目的停滞,出现这些问题也就没啥奇怪了。这才是自己最大的问题。

反思自己的问题很容易,改进才是难点。先从执行力入手,多思考产品,多学习,找准自己在团队中的位置。

生活

2013-10-20,准爸爸。这就是今年生活上最大的变化。

老实讲,在刚升级做爸爸的那一段时间,大概十月底到十一月中上旬,自己还没有做好这种变化的心理准备。突然之间许多事情扑面而来,回家生还是北京生,要不要在北京建档,要办各种证,要不要换房子等等,这些问题被自己的想象放大为很大的压力,感觉就要压垮自己,结果就是那一段时间脾气很不好,很容易上火生气,状态也定然不好,尽管已尽量克制,但还是影响到工作。

本来老婆应该是需要照顾的人,结果却反了过来让老婆开导我。还好我们比较顺利的解决了各个问题,尽我们能力去做,坦然面对。自己的状态也调整过来,做爸爸对男人来说就是一次心智再成熟的过程。


2014

工作

  1. 产品、管理的转型,提高执行力。
  2. 技术深度的挖掘。
  3. 一年一门新语言,Golang.

生活

  1. 迎接宝宝的到来。
  2. 带爸妈和妹妹旅游一次,带他们来北京?

CoreFoundation 和 NSObject 在 ARC 下的转换

CoreFoundation 有自己的引用计数处理方法,在 CF 下如果生成对象的方法中有 create、retain、copy 就表示 CF 会用自己的方式对引用计数加一,这就需要在结束的时候用 CFRelease() 释放。而 ARC 目前只对 NSObject 对象有自动的引用计数处理,所以在 ARC 如果有 CoreFoundation 对象和 NSObject 对象转换就需要用 __bridge, __bridge_transfer, __bridge_retained 进行引用计数管理的转换。

  • __bridge 表示 CF 对象和 NSObject 的引用计数平衡,无需转换管理权。适用于用不包含 create、retain、copy 的方法获取的 CF 对象转换为 NSObject。
  • __bridge_transfer 表示将 CF 对象的引用计数管理员转移到 NSObject 由 ARC 管理,无需再用 CFRelease() 释放。
  • __bridge_retained 表示将 NSObject 对象的引用计数管理权转移到 CF 管理,并且引用计数加一,那么在 CF 层就需要用 CFRelease() 释放该对象。

SDK 有两个宏 CFBridgingRetain, CFBridgingRelease 可以直接用,要注意 CFBridgingRetain 后要用 CFRelease() 释放。

// After using a CFBridgingRetain on an NSObject, the caller must take responsibility for calling CFRelease at an appropriate time.
NS_INLINE CF_RETURNS_RETAINED CFTypeRef CFBridgingRetain(id X) {
    return (__bridge_retained CFTypeRef)X;
}

NS_INLINE id CFBridgingRelease(CFTypeRef CF_CONSUMED X) {
    return (__bridge_transfer id)X;
}

参考 ARC工程转换和开发注意事项

Performance

在 V2EX 看到一个关于性能的讨论:

使用者优先,维护者其次,自己的偏执最不重要。

自己有时候就会过于偏执而掉到坑里,谨记。

Angular.js 学习笔记

When in Rome, do as the Romans do.

最近用 Angular “完整”做了一个服务的管理后台,完整的意思是整个 WebApp 都用 Angular MVW (Model-View-Whatever) 的思路去想去做。留一个 Angular 学习笔记。

Think in Angular

Angular 是个 框架(Framework),不像 jQuery、Underscore.js 是个库(Library),库的使用一般是在某个地方调用库所提供的方法完成想要的功能,而框架往往是控制应用整个 runtime 周期。所以 Angular 相对正确的使用方式是从应用全局开始,都用 Angular 提供的双向绑定、DI、Directive、Services 等,把应用数据逻辑层和页面 DOM 操作分离。用 Angular 首先就要认同接受并实践这种思路,Think in Angular

不错的 Angular 学习资料:

  1. 官方文档 AngularJS API Docs
  2. angular-phonecat,官方提供的入门教程,非常好的 Think in Angular 实践,建议把代码 clone 本地完整学习一遍。
  3. Angular FAQ.
  4. AngularUI,官方(?) UI 库,很多 directive 可用,比如 ui.bootstrap, ui.router, ui.grid.
  5. AngularJS Fundamentals In 60-ish Minutes 视频教程,官方 Youtube 频道 也有不少东西,不过看视频效率较低。
  6. AngularJS-Learning,收集了非常多文章,涵盖各个方面,实用参考。
  7. egghead.io Angular 视频教学。
  8. A Better Way to Learn AngularJS.

jQuery

How do I “think in AngularJS” if I have a jQuery background? 很详细的介绍了如果有 jQuery 开发背景在做 Angular 需要注意的地方,以下几点:

  1. 忘掉 jQuery,用 Angular 的方式解决。
  2. 知名的 jQuery plugin 一般都已经有 Angular directive 封装,首先尝试这些,真不能满足的话再用 jQuery 方式解决。
  3. 不要直接操作 DOM,试试 directive: ng-model, ng-class, ng-show, ng-hide, ng-disabled, ng-click.

Services

把常用的数据层访问封装成 Services 在 controller 之间共享访问。$resource 构造服务时要注意返回内容必须为单个对象或对象数组,如果服务端返回格式不符合可以用 $http 构造 service:

//make angular service with $http.
angular.module('APP.services', []).factory('AppsList', function($http){
  var AppsList = {
    list: function(){
      var promise = $http.get('/url').then(function(resp){
        return resp.data;
      });
      return promise;

      //or
      //return $http.get('/url');
      //because $http returns a promise.
    },
  };

  return AppsList;
});

//use
function AppCtrl(AppsList) {
  AppsList.list().then(function(data){
    console.log(data);
  });
}

$rootScope

通过 $rootScope 可以在所有 controller 之间共享方法:

angular.module('APP', []).run(function($rootScope){
  $rootScope.format_appinfo = function(data){
    //...
  };
});

//use in controller with $scope.
function AppCtrl($scope) {
  $scope.format_appinfo();
}

$routeParams

可以通过 $routeParams 获取 url 指定参数,比如

// route 设置 url 格式
$routeProvider.when('/app/:appid/:title', {controller:'AppCtrl'});

//use
function AppCtrl($routeParams) {
  var appid = $routeParams.appid;
  var title = $routeParams.title;
}

Filter

Angular 自带了很多 filters,比如 currency,date,json,lowercase/uppercase,其中 json 可以直接在页面上格式化展示对象信息,很方便检查调试:

<pre>
{.{ app | json }.} // Octopress/Jekyll 会把两个大括号格式化掉,所以中间加一点
</pre>

代码目录组织形式,参考 angular-seed。也可以用 Yeoman 进行管理。

├──css
│  └──app.css
├──img
├──index.html
├──js
│  ├──app.js                      //配置用到的所有 module,包括自定义。
│  ├──controllers                 //每个 controller 独立一个文件,以 Ctrl 结尾命名。
│  │  ├──app_info_ctrl.js
│  │  └──...
│  ├──directives.js
│  ├──filters.js
│  └──services.js
├──lib                            //CDN 没有的第三方库。
│  ├──ngProgress
└──partials                       //页面模版,文件名和 controller 相对应。
   ├──app_info.html
   └──...

更多参考:

  1. ng-newsletter 每周 Angular 最新技术周报,内容相当好。
  2. 8 Tips for Angular.js Beginners
  3. Migration guide for jQuery Developers
  4. Building Huuuuuge Apps with AngularJS

Bootstrap 3 Grid 笔记

作为非专业的前端开发,Bootstrap 真是个好东西,特别适合做运营、管理系统界面。这些系统界面要求不高,干净整齐就好,最主要的就是网格 Grid 的使用,简单记录一下。

Bootstrap 3 自带了移动优先的响应流式网格布局系统,将整个屏幕或某一视区(viewport)划分为 12 列,使用时把内容放到相应列中,自然就整齐不乱。一般来说 Bootstrap 的网页结构是 container > row > col,一个或多个 col 组成一组 row,多个 row 归于一个 container,这样的多级布局很方便在大小不同的屏幕灵活布局。

最小单位 col 有四种:col-xs-*, col-sm-*, col-md-*, col-lg-*,(题外话,Bootstrap 这四个 col 命名实在是让人费解,也不是什么缩写,完全没有 Foundation 的 small-2 large-4 columns 简洁明了),分别适用于手机(768px 以下),平板(768-992px),桌面(992px+)和超大屏幕(1200px+),后一位是需要的宽度比例,总和为 12,这样就自动把界面进行划分布局,比如想把一个普通电脑屏幕左右平分,两个 col-md-6 即可。col 可以组合,这样就同时适配手机和电脑,比如在电脑是左右三等分,手机是二等分:

<div class="container">
  <div class="row">
    <div class="col-md-4 col-xs-6">
      A
    </div>
    <div class="col-md-4 col-xs-6">
      B
    </div>
    <div class="col-md-4 col-xs-6">
      C
    </div>
  </div>
</div>

col 也可以嵌套,要注意的是每个 col 里又是一个 12 等分的完整网格,也要包在二级容器 row 里。比如左右二等分,每个再 1:2 划分:

<div class="container">
  <div class="row">
    <div class="col-md-6">
      <div class="row">
        <div class="col-sm-4">
          A
        </div>
        <div class="col-sm-8">
          B
        </div>
      </div>
    </div>
    <div class="col-md-6">
      ...
    </div>
  </div>
</div>

网格还有一个方便的东西就是 Offset .col-md-offset-*,可以把某一 div 向右偏移指定比例,比如只一半的宽度,然后居中,3-6-3 布局:

<div class="container">
  <div class="row">
    <div class="col-sm-6 col-sm-offset-3">
      S
  </div>
</div>

参考 Bootstrap 3: the new grid system, for starters.

批量杀进程

批量杀掉包含某一关键字的进程:

ps aux|grep KEY|grep -v grep|awk {'print $2'}|xargs kill -9

git cherry-pick

git-cherry-pick - Apply the changes introduced by some existing commits.

实际开发中会有这种情况:同时存在 v1、v2 两个分支,且不可合并。然后发现两个分支都存在某 bug,在 v1 中修复,需要合并到 v2,要么手动修改,或者用 git cherry-pick:

git cherry-pick 0ba264a1e666bacc