Build a Web Client with Nodejs: wireup AngularJs + Express + coffeescript + Jade + Less

廢話

有沒有這麼一個概念:「用同一套方式開發iOS、Android以及web?」隨著javascript的日益強大以及前端MVC的迅猛發展,這個概念變得越來越可行了。

一個完整的application通常包含web端以及mobile端,而web已經可以被視為iOS, Android以外的第三個「client」了。在技術的分類裡,iOS, Android, web backend的主流開發框架都已經是MVC了,惟獨剩下web frontend尚未完全「進化」成MVC。而近年來,前端MVC的優秀框架如雨後春筍般出現,像是AngularJs, Backbone, Ember …等,讓web frontend這塊紛亂之地也逐漸被MVC給攻克佔據…

一招MVC打天下,似乎不再遙不可及。

以前也是有開發過web,但在我的戰鬥經驗裡,web frontend的經驗值始終是初心者等級。現在既然有了MVC跟咖啡,是不是應該來練一下等級。用這麼酷的方式寫web我還是第一次,這些前端的MVC中,又以Google開發的angularjs最為火紅,(因為是Google大神開發的),有道是「站在巨人的肩膀上,才能看得更遠」不是嗎?所以這兩天便學了一下AngularJs,在此做個筆記~

先說說幾個開發時主要的component:

Express

Nodejs最火紅的套件之一,建立網站必備

Jade

類似rails的haml,擁有簡潔的語法,真的很簡潔,用過就會愛上,用來編譯成html

Less

類似rails的Sass,強化版的css語法,可以有變數,巢狀結構(這超重要),用來編譯成css

Coffeescript

Javascrip雖然強大,但其語法看就噁心,寫就想吐,debug更是會令人當場身亡! Coffeescript結合了ruby跟python的優點,是我看過語法最漂亮的程式語言,不只簡潔,而且有力,會幫你避掉許多Javascript語法設計不良的小毛病(例如global scope)。就像賽亞人+地球人=超強混血,沒什麼好講的,必備。

LiveReload

寫web最常做的事就是不停的reload reload re-re-re-re-reload頁面,re到手都快抽筋了,這個小工具可以讓生命更美好一點。它會在程式存檔的當下自動reload瀏覽器頁面(神奇吧),

AngularJs

前端主角,不解釋。

Notifier

Nodejs啟動時給一個桌面小提示,可含圖示跟聲音,不須切換視窗就能知道server啟動成功或失敗。 一樣是讓生命更美好的小工具

node-dev

一旦存檔就自動重啟nodejs server,搭配Notifier服用考試都100分。

bower

第三方js與css的套件管理程式,像是rails的gem,iOS的pod。意外嗎?一開始我也覺得意外,but… welcome frontend!

前面幾個component的setup在這篇文章有不錯的介紹,我就不打算贅述了,畢竟有好的輪子就不用再重新發明了是吧。(我決定繼承那篇文章然後結束這回合。)


檔案結構

一圖解千文

/api

放跟api server相關的程式。為什麼有api server? 因為我想把web搞得像client,就像mobile一樣ㄎㄎ

/bower_components

存放用bower這個前端套件管理程式所安裝的第三方套件,brow會自動創建此資料夾

/node_modules

存放用npm這個後端套件管理程式所安裝的第三方套件,npm會自動創建此資料夾

/routes

routing的規則,由於是thin server,所以只做很簡單的routing,大部份的routing會在AnglarJs裡做,在AngularJs尚未建立之前,還是需要由server供給一些基本的啟動檔案

/views

jade檔的家

/client

frontend端相關的檔案,AngularJs及Less的東西會分別存放在coffee/與less/裡,最後會compile成js與css,分別放在/public/js與/public/css資料夾裡,程式每次修改都要重來一次。(不要覺得很恐怖,這些過程都會全自動)

package.json

新增一個package.json檔,npm預設會找到這個檔案並安裝其內容,"*"代表安裝最新版本

然後

另外幾個比較特別的要安裝在系統比較方便,以便當指令用

node-dev

安裝完node-dev後新增一個檔案.node-dev.json,內容如下

這讓node-dev可以看懂coffee,不需要手動compile成js,寫咖啡必備

app.coffee

新版的express已經抽離less的模組,所以需要額外require 'less-middleware'。less-middleware能讓你coding時寫less,並讓client端自動收到css。由於我們的程式存放目錄跟user request的url不同,所以要額外做一些設定。我們這邊用lessDir作為less的來源資料夾,並用publicDir作為他compile成css後目的資料夾。在預設的情況下,當request http://domain/css/app.css時,less-middleware會去找lessDir/css/app.less,但很顯然我們的目錄結構並非如此,所以我們用他的preprocess功能,把/css濾掉,這樣才會找到lessDir/app.less,然後他也很聰明的是,將app.less compile完之後會直接放到回目的資料夾裏的正確置,也就是publicDir/css`。

同理connect-coffee-script也是。它能讓你coding時寫coffee並讓client自動收到js。他的檔案結構跟user request的路徑也不一樣,所以一樣要做額外設定。他們的原理大同小異,但設定機制有點不同,如有興趣可以去github看官方的說明。

最後宣布publicDir就是可以直接存取的靜態檔案,無需經過routing規則,也就是

app.use express.static(publicDir)

另外附帶一提,為了讓bower安裝的套件能更方便地被存取,再新增一個靜態檔案存取規則:

app.use '/bower_components', express.static(__dirname + '/bower_components')

當請求http://domain/bower_components/時,會對應到bowerDir/

為什麼要把事情搞得複雜?

這是一個好問題,如果把coffee/js,less/css都放在同一個地方事情會簡單許多,我通常也不喜歡事情變得複雜,但是在coding的時候維持一個好的檔案目錄結構是一件很重要的事,這會讓程式碼易於被理解,我認為目錄結構應該跟request url獨立開來。

全部通通放在public然後再用express去擋coffee跟less的請求也是個做法,但這樣”public”資料夾的存取規則就有了例外,而我討厭例外。

接著套入notifier, development only

再套入livereload, development only

目前整個app.coffee看起來像這樣:

啟動server


ng-app

新增一個layout.jade至/views

在html標籤宣告ng-app,讓AngularJs的作用範圍等同整個document頁面。至於必要的angular.js從google的cdn拿就可以了XD。

再新增一個item.jade至/views

目前就只有一個空的div(class="item")。注意到jade的的layout跟view是可以分開的,這把layout.jade從view中抽出,讓相依性進一步降低,rails也有類似的機制。

最後在末端載入config.js,這裡面會定義整個ng-app module,新增一個/client/coffee/config.coffee

這一行基本上宣告了一個名為theApp的module,後面搭配一個空陣列表示沒有用到其他額外的module。這邊的module可以理解成一個容器,將來許多angular的元件像是component, filter, service, …等都必須放置在這個容器裡,基本上就是一個top level的容器,之後一些必要的設定也會在此進行,所以我把檔名命名成config,也有很多人會把檔名命名成app,up to you。

ng-controller

新增一個/client/coffee/controllers.coffee

首先

把剛剛在config.coffee定義好的theApp module拿出來,而

則把這個ItemController放進容器裡。

constructor裡有一個$scope參數,這是angular內建的service,angular會在instantiate物件時自動把所需的內建service餵進去。所有$開頭的變數都是angular的內建service。這個$scope代表的是一個DOM標籤的scope,例如

這個$scope就代表整個div,然後$scope.item = ...就表示我們在這個div的範圍裡定義了一個名為item的angular變數。這邊有完整的service列表。

然後

用@getS3ImageUrl對item.photos做一點加工,並回傳一個新的array賦值回item.photos。這是coffee從python借來的list comprehension,酷吧! 其中@getS3ImageUrl的意思是this.getS3ImageUrl。

最後還有一個地方要修改,angular用關鍵字比對去認出那些內建的service像是$scope,但是在minify js檔案的時候變數名稱很可能都會被更動,如此便導致angular找不到關鍵字,解決這個問題的方式,就是我們同時把這個關鍵字也宣告成字串,這樣minify時就不會去動到它了。

改成

現在我們有了item,可以在頁面中呈現資訊了,修改/views/item.jade

首先link到controller.js,ItemController才會有定義,我們已經在ItemController裡定義了$scope.item,所以這邊就可以透過兩個大括號去access它{{item}}

這邊我們看到幾個用法

ng-hide

顧名思義,item存在時就這個tag就hide

ng-show

item存在時就這個tag就不hide,很ng-hide組合搭配很好用,能夠輕易地控制頁面內容,無需手動jQuery操作DOM

ng-repeat

這個ng-repeat一樣是用了跟coffee極度類似的list comprehension表示式,咖啡懂了這個就懂了。順帶一提ng-repeat跟ng-src不一定要在同一個tag,例如也可以這樣

Apple Push Notification Service (APNS): How your server push notifications to your apps?

Say, you have your own server, you want to push notifications to your apps, how to do that? Well, basically it should be easy because Apple do that for you by Apple Push Notification Service (APNS). But it’s not quite easy enough because the authentication process is cumbersome.

It’s all about authentication

Your Server -> APNS -> Your iOS apps

Under your provisioning profile, you have an App ID, if you want to enable the Push Notification, you need to create a certificate for it.

This is the way you do:

  • Create public/private key.
  • Create certificate signing request (CSR)
  • Upload the CSR to Apple to create certificate
  • Download the certificate
  • Convert the certificate to cert.pem
  • Convert private key to key.pem
  • Feed key.pem and cert.pem to your server
  • Get device token and upload it to your server
  • Start push notification in your server

CSR vs Certificate:

CSR is a request that you tell Apple you wan to get a certificate. You signed on it (with private key) first, submit to Apple and after Apple agrees your request, he generates a certificate and signed on it and give it to you.

Basically, a certificate is just like a paper that signed with you and Apple. It is a registration of your private key and your private key somehow represent yourself. It’s all about an identity of YOU.

Convert the certificate to cert.pem

In keychain access, find the certificate named “Apple Development iOS Push Server: appID”, export (cmd+shift+E) to dev_cert.p12.

Convert private key to key.pem

In keychain access, export the private key associated with the certificate to dev_key.p12.

And make it unencrypted:

The Client Side

The client side is relatively easy: just tell iOS you want to register remote notification and implement some delegate functions.

This method will show up an alertView asking the user to agree with your push notification request, if the user disagree, you’ll get no tokens.

A very nice ref: iOS用戶端的APNS服務簡介與實現

And some nice refs that are helpful to understand the concept:

Fastest: Nginx + WordPress

Fastest Approach

Recently, I found this nice repo, which is a linux script helping you build a website based on nginx/php/mysql/wordpress (or non-wordpress).

It’s awesome, quick and easy.

To see how awesome it is:

And visit http://example.com.

Live 1080p video streaming from the Raspberry Pi to browsers using nginx and rtmp

Gstremer + RTMP + Browser

This approach is mainly referenced from here except that I use gstraemer as streaming/encode provider instead of ffmpeg. Gstreamer is an awesome media framework for streaming service. It is not only powerful but also flexible. The plugin-based architecture makes it very flexible in any situations, from codecs to communication protocols.

Install Gstreamer

Gstreamer are composed by a gstreamer core and some plugins libraries. Installation please reference this post. Notice that we’re going to use gstreamer’s rtmp module, which is contained in gst-plugins-bad, you must make sure that you have librtmp-dev installed before you compile gst-plugins-bad.

Install Nginx & Nginx-rtmp module

While gstreamer are good at video encoding and streaming, it’s not probably the best server solution facing the public. We need nginx to re-transmit the rtmp packets to allow multiple client connections.

Make sure /etc/nginx is empty because make install will not overwrite files which already exists.

Make a temporary folder to build codes

Check nginx version and start server.

Setting nginx

Edit nginx.conf

Restart server

Install flash rtmp player: StorbeMediaPlayback

For client side playback, I use strobe media playback which is an open source media framework from adobe. It seems that it’s not maintained since 2012, but it works great!

Some helpful video tutorial here

setup index.html

Edit /var/www/html/index.html and modify {pi_address} to valid address

Start Streaming

Pipe video stream from gstreamer to nginx by rtmp

Open browser and visit

Status Monitor

Demo (OSX):

Open Chrome, Safari, Firefox at the same time.
Screen Shot 2014-09-16 at 下午5.06.02

Backup everythin

Here’s a very nice post for backing up SD card as an image.

讓brew更方便一點

OSX的套件管理工具brew可以說是相當方便的工具,brew有時候(or always?)會下載source code來build,放在他自己的資料夾/usr/local/Cellar/,然後再link到系統目錄(通常包括但不限於/usr/local/opt/)。

但是就在link這個階段,常常需要sudo權限去寫系統目錄,而brew本身為了安全考量,不能直接使用sudo brew,除非brew執行檔的擁有者是root。這樣安全歸安全,但每次link都要改目錄(而且很可能不只一個)的寫入權限,link完再改回來,實在是很不方便。

如果你跟我一樣也喜歡簡單一點的人生,就把brew擁有者改成root吧! (At your own RISK!!)

Compile & Install Gstreamer on Raspberry Pi

Prerequisite

Download these
* gstreamer/gstreamer
* gstreamer/gst-plugins-base
* gstreamer/gst-plugins-good
* gstreamer/gst-plugins-bad
* gstreamer/gst-plugins-ugly

from GStreamer source, at the time writing this post, the latest version I use is 1.4.1

For gst-plugins-base, gst-plugins-good and gst-plugins-ugly, simply

For gst-plugins-bad, there’s a little problem during normal installation. In old times, gst-plugins-gl was required for gst-plugins-bad and now it is merged into gst-plugins-bad, at least they claim so, but somehow there’s some bug in there at this moment, which cause gst-plugins-bad could not find gst-plugins-gl and gst-plugins-gl cannot be installed due to the fact that the author thinks there’s no need to install it since it has been merged into gst-plugins-bad.

So we need some hack for this. Fortunately, this thread has already worked it out. Follow those steps and you should get rid off the trouble.

Edit environment variable

in ~/.bashrc add a line

and reload bash file:

Test command:

If you’ve installed rpicamsrc:

else:

如何一次控制多台ec2 server

Introduce fabric

這是一個類似ruby capistrano的軟件,專門用來處理deploy的事,也可以用來命令多個伺服器做事情。

使用

新增一個fabfile.py檔案,fab程式會「自動找當前目錄的fabfile.py檔案去執行」。

fabfile.py:

解說

這一段設定fab的連線環境,hosts可以是包含多個server address的array,方便吧!除此之外,fabfile.py的內容其實就是一般的python,比較特別的是,用fab指令執行的function會依序在每台機器上執行。例如在本機電腦輸入

然後你會看到fab依序連線到每個host,並執行hello(),然後將輸出顯示在local terminal。這很適合為每台機器做一些初始設定,像是init()裡面做的事。

然而,目前還不曉得如何在不同server給不同的function argument進去。待續…

透過ssh遠端使用sublime

sublime用慣了,每次連到server都要換成vim實在是很不習慣,不停地尋找最有效率的方式工作,難道不是軟體工程師的本能嗎?

開始用sublime編輯remote的檔案吧!!

Local端

  1. 安裝sublime的rsub套件,cmd+shift+p,選擇install package叫出套件管理程式,然後搜尋rsub,安裝
  2. 編輯你的ssh config,新增一個

Server端

  1. 安裝rsub

開始使用

local端用剛剛的ssh登入server:

然後在server端打開檔案

接著就會看到local端跳出sublime,編輯你的remote file,只要local端一儲存,remote也跟著儲存,酷吧!!

如何中文化bbpress?

鑑於許多教學都已經過期不適用,這邊稍微記錄一下

首先下載bbpress的繁體中文翻譯檔,然後把它放到這裡面:

說明

wordpress會基於wp-config.php裡的語系設定,到這個路徑/wp-content/languages/plugins去找

這兩個檔案,也就是如果要「xxx」這個plugin的「yyy」語系檔,那麼就把xxx-yyy.po與xxx-yyy.mo放進這個資料夾裡就可以了