2015年3月31日 星期二

利用 Google Form 處理活動登記

《陳僖儀慈善基金》獲得基督教香港信義會的邀請,參與 2015 年 4 月 11 日的賣旗日。這將會是基金第一個公開的義工活動。已登記的義工可以透過電郵報名參加。

我在想:「這豈不是會收到很多的電郵?雖然可以為收件匣加入「過濾條件」把郵件移到一個指定的目錄,但由於大會沒有說明清楚格式,相信分類起來不能完全自動化。除了要一封一封地看之外,還要逐個逐個整理義工編號,所花的時間將會很多!」。其實這樣的情況,Google Form 可以幫得上忙:

1. 在 Google Drive 上建立新的 Google Form


2. 首先是設定表格,沒有甚麼好打勾


3. 輸入第一條問題。由於要知道是哪位義工參加,所以要輸入義工編號。同時作簡單的檢查,編號中一定要出現「SCF-」


4. 由於義工們未必有 Google 帳戶,所以 Google Form 的地址是要公開讓所有人都能進入。在這種情況下,為免被人亂填資料,所以要申請者輸入在《陳僖儀慈善基金》登記的電郵地址作為驗證之用。同時也設定電郵格式檢查。以上兩條題目都是「必答」


5. 之後就是填寫完畢後顯示的句子


6. 點擊「Send form」後,Google 會詢問你資料要儲存在哪裡。我認為放到會員清單的試算表會較好。除了一目瞭然外,還能讀取試算表內其他表格的內容,如:驗證用之電郵地址


7. 完成

按照昨天介紹的《利用 Google Spreadsheet 作批次性電郵通知》把表格發給義工。當他們打開電郵中的連結時會看到如下畫面:


填妥完畢後出現跟設定一樣的「多謝您的參與!我們會有專人跟您聯絡!」句子


打開會員清單的試算表會發現多了一張新表格,內容則是義工們的回覆


最後,只要加入程式代碼驗證義工的電郵地址,顯示結果,甚至是聯絡電話,便能非常方便地管理每一次的活動。

2015年3月30日 星期一

利用 Google Spreadsheet 作批次性電郵通知

零晨 12:52 分,收到由《陳僖儀慈善基金》發出的義工登記確認電郵。我在想:「時候已經不早,還在忙基金的工作,真的很付出!難道電郵是一封一封地輸入?」。基金會是用 Gmail 的,亦即是說能使用 Google Docs。在 M$ 時代,已經能混合 Excel 及 Word 去做批次處理;現今這個世界,應該是更簡單方便。於是向 Google 老師學習一下。

我的想法是對。單單利用 Google Spreadsheet 加上 Google Apps Script 已經能作出批次性電郵通知。亦即是說可以按照 Spreadsheet 內的資料發出電郵。電郵內容可以是全部人一式一樣,也可以加入個人化訊息。我模擬了一下發送義工登記確認電郵。步驟如下:

1. 建立一張新的 Spreadsheet,內容大約如下:


2. 點選 Tools 菜單內的 Script editor...


3. 畫面右方是編程區,輸入以下程式:
//-----------------------------------------------------------------------------
//  Member List Functions
//  Copyright 2015 Pacess HO.  All rights reserved.
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
function sendEmail()  {

  var sheet = SpreadsheetApp.getActiveSheet();
  var startRow = 3;
  var endRow = sheet.getLastRow();
  var endColumn = sheet.getLastColumn();
  Logger.log(endRow);

  //  getRange(row, column, numRows, numColumns)
  var dataRange = sheet.getRange(startRow, 1, endRow-startRow+1, endColumn)
  var data = dataRange.getValues();
  for (i in data)  {

    var row = data[i];

    //  Skip if already sent
    var sentStatus = row[endColumn-1];
    if (sentStatus == undefined || sentStatus.length >= 10)  {continue;}

    //  Member name must more than 2 bytes, otherwise skip
    var memberName = row[1];
    if (memberName.length <= 2)  {continue;}

    var memberID = row[0];
    var memberEmail = row[2];

    var subject = "Sending emails from a Spreadsheet";
    var message = memberName+"您好,\r\n\n歡迎您的加入!您的義工編號為"+memberID+"\r\n\n陳僖儀慈善基金";
    MailApp.sendEmail(memberEmail, subject, message);

    //  Mark sent
    var timeStamp = "'"+getCurrentDateTimeString();
    sheet.getRange(startRow+parseInt(i), endColumn).setValue(timeStamp);
    SpreadsheetApp.flush();
  }
}

//-----------------------------------------------------------------------------
function getCurrentDateTimeString()  {    
 return getDateTimeStringFromDate(new Date());
}

//-----------------------------------------------------------------------------
function getDateTimeStringFromDate(date)  {
 var year = date.getFullYear();
 var month = date.getMonth()+1;
 var day = date.getDate();
 var hours = date.getHours();
 var minutes = date.getMinutes();
 var seconds = date.getSeconds();
 
 var dateTimeString = year+"-"+padZeros(month, 2)+"-"+padZeros(day, 2)+" "+padZeros(hours, 2)+":"+padZeros(minutes, 2)+":"+padZeros(seconds, 2);
 return dateTimeString;
}

//-----------------------------------------------------------------------------
function padZeros(number, width)  {
 number = ""+number;
 return (number.length >= width) ? number : new Array(width-number.length+1).join("0")+number;
}
4. 儲存後點擊甲蟲左面的三角形按鈕執行程序


5. 由於用上了發送電郵功能,在第一次執行時會要求受權


6. 點選「Accept」確定受權


7. 回到 Spreadsheet 等一會便會看到發送電郵的時間


這個程式考慮到在很大的負荷時可能會出現當機問題,因此會以 Spreadsheet 最右那行作為判斷。如果最右的格子內沒有內容便代表還沒發送電郵,會進行處理;若有內容的話,則會視作已發送而跳到下一行。至於內容方面,則定義在程式中的 message 變量,只要作出修改,便能改變電郵內容。

2015年3月29日 星期日

為陳僖儀而填的《陳僖儀》

陳僖儀的死忌快到了,今年沒有為她創作甚麼。不過,從《陳僖儀慈善基金》簡介會得到啟發,讓我有動力去為一首陳僖儀所創作的歌曲譜上新詞。我希望基金會能用上這首曲作為會歌;也希望她的歌手好友能演繹這一首歌;更希望這一首歌能收錄成為大碟中的一首曲目,將收益撥捐到《陳僖儀慈善基金》作為慈善用途。詞的水準不夠,但希望心意搭救,讓更多人認識陳僖儀這個女孩。
歌名:陳僖儀
原曲:5554343213
作曲:陳僖儀
填詞:啤蛇蛇

因她 真心對著我 無私熱情
還有她甜美的聲線
能令我心呼應

因她 的心 才可破冰
縱使因 跌過後 亦再度衝線
我亦會想 為何令她 真心的這樣為我
難道我 曾為她做過甚麼?

原先我亦放下不去想
原先我亦想極想不通
在這危難時候 原來還有 這樣的一個人

如果我亦決定要跟她
如果我亦約定畀心機 會點?
相信感染到 我及你

讓那空虛的心
讓這歡呼的心
流著熱情 能讓世間 變得美好

2015年3月28日 星期六

《陳僖儀慈善基金》簡介晚會回顧


原本打算出席《陳僖儀慈善基金》簡介晚會作為一份支持。得知地點就在我公司旁,時間也是我放工後,簡直有點為我而設的感覺,不能不去。大會簡單介紹了基金架構。同場的食物中還有 Sita 喜愛的咕嚕肉及魚香茄子。根據 Sita 妹妹 Crystal 的報告,之前聯署希望 e 道愎聲的情況,得知已經得到當局同意。我亦藉著今日過澳門教書時測試一下,果然再次聽到陳僖儀的聲音。

2015年3月24日 星期二

新專案名字的構思

公司為一個新專案立項,需要一個應用名稱。作為創辦人之一,想出了三個名字。當中兩個都是帶點抄襲成份的新創名字;而第三個則用上同音異字的方法。看上去,三個名字都不起眼。但發現只要為名字加入故事或理念,便能令人有深刻的感受。就像 Apple 沿用開的說故事、說理念方式,讓用戶有更深層的認識與認同。我們以一人兩票的方式選取,第三個名字亦因此而大比數獲得認同。

這個名字除了因為故事或理念而獲得印象外,還能以符號作為形像標記,帶起了整個名字的中心思想,也帶出了一點反傳統的觀念。目前我們都很滿意項目名稱,下一步便是為項目設計商標,希望能繼續秉承名字的創意,製作出一見難忘的圖案。

2015年3月23日 星期一

窗花花盆托架


小菊花放在屋外一天後,已經謝了很多,花兒也由直變成孿。心想:「是不是因為暴曬的後果?明明店員說不怕曬呢!」。只好想辦法把它移進屋內。可是香港的地方寸金尺土,哪有地方?!唯有打印一個支架,讓它把整個花盆托掛在窗花上。打印後,總算成功掛上。希望小菊花能由孿變回直吧!

2015年3月22日 星期日

晾衫架上的花盤托


在旺角花墟買了小菊花回來,冷氣機附近的位置已被數株植物佔據,沒有其他能有充足日照的地方可以放置小菊花。於是相到了荒廢了的晾衫架。那是以游繩構造的晾衫架。只要打印一個放在游繩中的托架,便能承托花盆。量度好尺寸後,成功打印出來,也順利地放置好小菊花。

2015年3月19日 星期四

衣櫃門托架


在 IKEA 購買的衣櫃用了七年後開始老化。其中一趟衣櫃門的膠水失去黏力,玻璃跟木框分離了。由於款式已經沒有再發售,而且市面上的櫃門尺寸又不同了,無法購買另一趟門取代;所以我畫了一個托架,希望能修理一下櫃門。

2015年3月18日 星期三

建立 Ruby on Rails 專案

公司曾經有一個項目以 Ruby on Rails 程式作為服務器端,當時學過一點點 ROR,但沒有動手做,老人家已經忘記得一乾二淨。然而,最近在學習 Big Data 的過程中,在 Coursera 發現了「Web Application Architectures」這個課程,當中教授 Ruby on Rails 的知識;老師的話容易入腦,再一次引起學習 ROR 的興趣。

為怕忘記,還是做一點筆記。以下是建立專案的步驟:
MacBook-Pro:Ruby-on-Rails pacess$ mkdir blog
MacBook-Pro:Ruby-on-Rails pacess$ cd blog/

然後設定所使用的 Ruby 版本及建立專案需要用到的 Gemset:
MacBook-Pro:blog pacess$ rvm use ruby-2.2.0@blog --ruby-version --create
ruby-2.2.0 - #gemset created /Users/pacess/.rvm/gems/ruby-2.2.0@blog
ruby-2.2.0 - #generating blog wrappers..........
Using /Users/pacess/.rvm/gems/ruby-2.2.0 with gemset blog

安裝 Rails:
MacBook-Pro:blog pacess$ gem install rails
Fetching: rails-4.2.0.gem (100%)
Successfully installed rails-4.2.0
1 gem installed

之後就是建立 Rails starter app 的檔案:
MacBook-Pro:blog pacess$ rails new .
       exist  
      create  README.rdoc
      create  Rakefile
      create  config.ru
      create  .gitignore
      create  Gemfile
      create  app
      create  app/assets/javascripts/application.js
      create  app/assets/stylesheets/application.css
      create  app/controllers/application_controller.rb
      create  app/helpers/application_helper.rb
      create  app/views/layouts/application.html.erb
      create  app/assets/images/.keep
      create  app/mailers/.keep
      create  app/models/.keep
      create  app/controllers/concerns/.keep
      create  app/models/concerns/.keep
      create  bin
      create  bin/bundle
      create  bin/rails
      create  bin/rake
      create  bin/setup
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/secrets.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/cookies_serializer.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.rb
      create  config/initializers/session_store.rb
      create  config/initializers/wrap_parameters.rb
      create  config/locales
      create  config/locales/en.yml
      create  config/boot.rb
      create  config/database.yml
      create  db
      create  db/seeds.rb
      create  lib
      create  lib/tasks
      create  lib/tasks/.keep
      create  lib/assets
      create  lib/assets/.keep
      create  log
      create  log/.keep
      create  public
      create  public/404.html
      create  public/422.html
      create  public/500.html
      create  public/favicon.ico
      create  public/robots.txt
      create  test/fixtures
      create  test/fixtures/.keep
      create  test/controllers
      create  test/controllers/.keep
      create  test/mailers
      create  test/mailers/.keep
      create  test/models
      create  test/models/.keep
      create  test/helpers
      create  test/helpers/.keep
      create  test/integration
      create  test/integration/.keep
      create  test/test_helper.rb
      create  tmp/cache
      create  tmp/cache/assets
      create  vendor/assets/javascripts
      create  vendor/assets/javascripts/.keep
      create  vendor/assets/stylesheets
      create  vendor/assets/stylesheets/.keep
         run  bundle install
Fetching gem metadata from https://rubygems.org/..........
Fetching version metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.4.2
Using i18n 0.7.0
Using json 1.8.2
Using minitest 5.5.1
Using thread_safe 0.3.5
Using tzinfo 1.2.2
Using activesupport 4.2.0
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile 0.6.2
Using nokogiri 1.6.6.2
Using rails-deprecated_sanitizer 1.0.3
Using rails-dom-testing 1.0.6
Using loofah 2.0.1
Using rails-html-sanitizer 1.0.2
Using actionview 4.2.0
Using rack 1.6.0
Using rack-test 0.6.3
Using actionpack 4.2.0
Using globalid 0.3.3
Using activejob 4.2.0
Using mime-types 2.4.3
Using mail 2.6.3
Using actionmailer 4.2.0
Using activemodel 4.2.0
Using arel 6.0.0
Using activerecord 4.2.0
Using debug_inspector 0.0.2
Using binding_of_caller 0.7.2
Using bundler 1.8.5
Using columnize 0.9.0
Using byebug 4.0.2
Using coffee-script-source 1.9.1
Using execjs 2.4.0
Using coffee-script 2.3.0
Using thor 0.19.1
Using railties 4.2.0
Using coffee-rails 4.1.0
Using hike 1.2.3
Using multi_json 1.11.0
Using jbuilder 2.2.11
Using jquery-rails 4.0.3
Using tilt 1.4.1
Using sprockets 2.12.3
Using sprockets-rails 2.2.4
Using rails 4.2.0
Using rdoc 4.2.0
Using sass 3.4.13
Using sass-rails 5.0.1
Using sdoc 0.4.1
Using spring 1.3.3
Using sqlite3 1.3.10
Using turbolinks 2.5.3
Using uglifier 2.7.1
Using web-console 2.1.2
Bundle complete! 12 Gemfile dependencies, 55 gems now installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted

2015年3月17日 星期二

在 OS X 安裝 Homebrew

Mac OS X 內建了 Ruby,利用它可以安裝 Homebrew:
MacBook-Pro:~ pacess$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
==> This script will install:
/usr/local/bin/brew
/usr/local/Library/...
/usr/local/share/man/man1/brew.1
==> The following directories will be made group writable:
/usr/local/.
/usr/local/bin
/usr/local/include
/usr/local/lib
/usr/local/lib/pkgconfig
/usr/local/share
/usr/local/share/man
==> The following directories will have their group set to admin:
/usr/local/.
/usr/local/bin
/usr/local/include
/usr/local/lib
/usr/local/lib/pkgconfig
/usr/local/share
/usr/local/share/man

Press RETURN to continue or any other key to abort
==> /usr/bin/sudo /bin/chmod g+rwx /usr/local/. /usr/local/bin /usr/local/include /usr/local/lib /usr/local/lib/pkgconfig /usr/local/share /usr/local/share/man
Password:
==> /usr/bin/sudo /usr/bin/chgrp admin /usr/local/. /usr/local/bin /usr/local/include /usr/local/lib /usr/local/lib/pkgconfig /usr/local/share /usr/local/share/man
==> /usr/bin/sudo /bin/mkdir /Library/Caches/Homebrew
==> /usr/bin/sudo /bin/chmod g+rwx /Library/Caches/Homebrew
==> Downloading and installing Homebrew...
remote: Counting objects: 237423, done.
remote: Compressing objects: 100% (1040/1040), done.
remote: Total 237423 (delta 711), reused 0 (delta 0), pack-reused 236381
Receiving objects: 100% (237423/237423), 32.52 MiB | 1.14 MiB/s, done.
Resolving deltas: 100% (176649/176649), done.
From https://github.com/Homebrew/homebrew
 * [new branch]      master     -> origin/master
Checking out files: 100% (3499/3499), done.
HEAD is now at 0faf905 Return early for the == case in Version#<=>
==> Installation successful!
==> Next steps
Run `brew doctor` before you install anything
Run `brew help` to get started

2015年3月16日 星期一

解決 Apple Watch 的 Unhandled Category


Apple Watch 應用程式最主打的一個功能是:推送通知。然而,這個推送通知跟 iOS 應用程式的有一點點不同。我們可以為推送通知設定不同的類別(Category);而每個類別可以有各自的畫面結構,甚至顏色。而畫面又分成靜態(Static)及動態(Dynamic)兩款。要留意一點,要是畫面的載入時間過長時,Apple Watch 會自行載入靜態畫面取代。

類別的名稱可由開發人員自行在 Storyboard 定義;只要推送通知內容標明相同名稱就可以,要是傳給了沒被定義的名稱時,就會出現以下畫面:

到時只要在 Storyboard 定義好遺漏的類別名稱;或在推送通知的類別內容填上定義好的名稱便能解決問題。

2015年3月14日 星期六

《陳僖儀慈善基金》簡介晚會


為了讓更多人認識《陳僖儀慈善基金》的運作、了解成立目的、義工活動...等,大會將於 2015 年 3 月 27 日晚舉行「陳僖儀慈善基金簡介晚會」。有興趣的朋友可以瀏覽《陳僖儀慈善基金》的 Facebook 網頁

2015年3月13日 星期五

解決 Swift 的編譯問題


Apple Watch 發售在即,Apple 特意地搞了一個 Watch App 試驗,讓應用開發人員能在發售前能在實機上測試自己的程式。雖然我遲了報名,但還算取得測試名額。於是今日開始編寫簡單的測試程式。

程式名稱隨手定義為 WatchKit。可是在編譯時屢屢出現錯誤。檢查過後沒有異樣的地方,不論是 Import, Syntax 還是設定,都跟其他範例一樣。根本不知道問題所在。後來我重新建立項目,使用別的名字,問題就不會出現。證明是 WatchKit 名稱出事...。

2015年3月11日 星期三

解決 iPhone 4 無限花花問題


很久沒用的 iPhone 4 已經沒電,於是把它插進充電器,作為定期保養。可是接上後不久便出現無限花花情況,進入不了鎖機畫面,甚至是 Springboard。

花了點時間解決。我把 iPhone 改為接上 MacBook Pro USB,利用 TinyUmbrella 把 iPhone 4 踢進回復模式。我的 iPhone 4 仍然保留著 iOS 5.0.1,不打算升級。實情是除了保留各代 iPhone 外,我還保留著運行得到的各代 iOS。特別是我喜愛擬物風格的 iOS 版本。

把 iPhone 4 踢進回復模式後便能終止無限花花情況。之後只要同樣用 TinyUmbrella 把它踢出回復模式,手機便能正常地回到鎖機畫面。

2015年3月10日 星期二

《陳僖儀慈善基金》成立


今日是 Sita 陳僖儀的 28 歲生日。今年沒有聚會;但籌備了一年的《陳僖儀慈善基金》開始運作。將 Sita 的愛心傳揚開去。我曾承諾過若公司能創造營利獲分發花紅時,會拿 10% 出來貢獻社會,《陳僖儀慈善基金》將會成為目標。希望這一天能快快到來。

2015年3月9日 星期一

修正 iOS 8 的 OTA 安裝錯誤

四年前,我已經把所有開發出來的 IPA 以 OTA 形式給客戶下載,一直沿用至今。但最近發現有些 iPhone / iPad 說算記錄了 UDID 還是無法成功安裝。問題也出現得很鬼秘,在點擊 OTA 連結後選「安裝」,沒有任何事情發生。應用沒有被下載及安裝。查看運作記錄,發現問題:
Mar 09 16:22:17 iPad-Air-Pacess itunesstored[91] <Warning>: LoadExternalDownloadManifestOperation: Ignore manifest download, already have bundleID: com.pacess.good.game

這是 iOS 8 的錯誤。我的解決方法是把 OTA 的 Plist 內的 bundle-identifier 尾加入「.fix」來把問題處理掉。
<key>bundle-identifier</key>
<string>com.pacess.good.game.fix</string>

2015年3月8日 星期日

垂直升降設計


最近在機體研發方面沒有新搞作,停得久,身癢。決定要設計新的機體。今次打算使用 Raspberry Pi 2 作為機體核心,撘配其他控制板處理硬件上的動作、鏡頭、多輻加速度檢測器、WiFi 模組...等,務求打造出一台能執行複雜指令及處理的機器;更希進能發展出自律性用途。其中一個重點結構設計是參考 iRoad 溜冰型態的垂直升降設計。由於對設計的概念不多,於是拿「樂高」來砌出模型,判斷那個方式是最有效、負重情況較輕的垂直升降設計。

2015年3月6日 星期五

第一屆香港廢柴機械人大戰


朋友知道我熱愛機械人,知道有《第一屆香港廢柴機械人大戰》便第一時間通知。我做事比較認真,對於機體方面也希望製作實用的為主。不過,為娛樂輕鬆製作一部機體倒也無妨,只是廢得來又不想毫無用處,想爆頭也想不到能滿足這個設定的機體,原來「廢」都不是一件易事。有興趣的朋友可點擊這裡

2015年3月5日 星期四

修改 OpenCart 1.5.6 的「發送電郵」Bug...


弟弟今日找我求助,事緣是他為自己的玩具售賣網站買入了「Newsletter Enhancements v3.7.2」模組在安裝後出現問題。我花了兩個小時也無法修正它的問題,需要電郵給開發公司尋求解決方法。弟弟會買 Newsletter Enhancements v3.7.2 是因為 OpenCart 的內建「發送電郵」有 Bug,填好所有資料後無法送出電郵,更指沒有輸入內文。於是我幫忙看一下。很快發現是 Javascript 的邏輯出現問題。明明內容是在 CKEditor 中輸入,檢查卻以一個隱藏了的 Textarea 內文為準,那當然會沒有內容!於是我把它的代碼修正後,便能正常發送電郵。

發生問題的檔案位於 /admin/view/template/sale/contact.tpl。在第 243 行作以下修改:
function send(url) { 
 //  This is original program bug
// $('textarea[name=\'message\']').html($('textarea[name=\'message\']').val());

 //  Fixed by Pacess at 2015-Mar-05
 var message = '';
 for (instance in CKEDITOR.instances) {
  message = CKEDITOR.instances[instance].getData();
 }
 $('textarea[name=\'message\']').html(message);

2015年3月3日 星期二

在 CentOS 下安裝 Node.js 模組

昨天安裝好 Node.js 後,嘗試了 http 庫,能很簡單地印出 HTTP 回應。在網上找到評論說「http 之於 connect,就如 connect 之於 express」,所以今天試試安裝 express。首先跳到安裝好 Node.js 的目錄:
[root@centos ~]# cd /usr/local/node-v0.12.0-linux-x64/
利用 npm 安裝 express:
[root@centos lib]# npm install express
express@4.12.2 node_modules/express
├── merge-descriptors@1.0.0
├── utils-merge@1.0.0
├── cookie-signature@1.0.6
├── methods@1.1.1
├── fresh@0.2.4
├── cookie@0.1.2
├── escape-html@1.0.1
├── range-parser@1.0.2
├── finalhandler@0.3.3
├── content-type@1.0.1
├── vary@1.0.0
├── parseurl@1.3.0
├── serve-static@1.9.1
├── content-disposition@0.5.0
├── path-to-regexp@0.1.3
├── depd@1.0.0
├── on-finished@2.2.0 (ee-first@1.1.0)
├── qs@2.3.3
├── debug@2.1.2 (ms@0.7.0)
├── proxy-addr@1.0.6 (forwarded@0.1.0, ipaddr.js@0.1.8)
├── send@0.12.1 (destroy@1.0.3, ms@0.7.0, mime@1.3.4)
├── accepts@1.2.4 (negotiator@0.5.1, mime-types@2.0.9)
├── etag@1.5.1 (crc@3.2.1)
└── type-is@1.6.0 (media-typer@0.3.0, mime-types@2.0.9)
完成,非常簡單。由於新版 express 已沒有包含 Cookie Parser,要特別自行安裝:
[root@centos node.js]# npm install cookie-parser
cookie-parser@1.3.4 node_modules/cookie-parser
├── cookie-signature@1.0.6
└── cookie@0.1.2
除了 express 之外,我還想試試 Socket 通訊,因此還安裝了 Socket.io:
[root@centos lib]# npm install socket.io
 
> ws@0.5.0 install /usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/engine.io/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

make: Entering directory `/usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/engine.io/node_modules/ws/build'
  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
  SOLINK_MODULE(target) Release/obj.target/bufferutil.node
  SOLINK_MODULE(target) Release/obj.target/bufferutil.node: Finished
  COPY Release/bufferutil.node
  CXX(target) Release/obj.target/validation/src/validation.o
  SOLINK_MODULE(target) Release/obj.target/validation.node
  SOLINK_MODULE(target) Release/obj.target/validation.node: Finished
  COPY Release/validation.node
make: Leaving directory `/usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/engine.io/node_modules/ws/build'

> ws@0.4.31 install /usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/socket.io-client/node_modules/engine.io-client/node_modules/ws
> (node-gyp rebuild 2> builderror.log) || (exit 0)

make: Entering directory `/usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/socket.io-client/node_modules/engine.io-client/node_modules/ws/build'
  CXX(target) Release/obj.target/bufferutil/src/bufferutil.o
make: Leaving directory `/usr/local/node-v0.12.0-linux-x64/lib/node_modules/socket.io/node_modules/socket.io-client/node_modules/engine.io-client/node_modules/ws/build'
socket.io@1.3.5 node_modules/socket.io
├── debug@2.1.0 (ms@0.6.2)
├── has-binary-data@0.1.3 (isarray@0.0.1)
├── socket.io-adapter@0.3.1 (object-keys@1.0.1, debug@1.0.2, socket.io-parser@2.2.2)
├── socket.io-parser@2.2.4 (isarray@0.0.1, debug@0.7.4, component-emitter@1.1.2, benchmark@1.0.0, json3@3.2.6)
├── engine.io@1.5.1 (base64id@0.1.0, debug@1.0.3, engine.io-parser@1.2.1, ws@0.5.0)
└── socket.io-client@1.3.5 (to-array@0.1.3, indexof@0.0.1, debug@0.7.4, component-bind@1.0.0, backo2@1.0.2, object-component@0.0.3, component-emitter@1.1.2, has-binary@0.1.6, parseuri@0.0.2, engine.io-client@1.5.1)
同樣很順利地完成。npm 就像 ROR 的 Gem 一樣,十分方便好用。

2015年3月2日 星期一

在 CentOS 安裝 Node.js

最近不斷有新東西衝擊我的腦袋,有:CakePHP, Symfony, Flux, Twig, Unity, PHP-FPM, Node.js, WordPress...等等全部都想學。今日就先試試在 CentOS 下安裝 Node.js。

首先是下載 Node.js:
[root@centos ~]# wget http://nodejs.org/dist/v0.12.0/node-v0.12.0-linux-x64.tar.gz
--2015-03-02 17:38:25--  http://nodejs.org/dist/v0.12.0/node-v0.12.0-linux-x64.tar.gz
Resolving nodejs.org... 165.225.133.150
Connecting to nodejs.org|165.225.133.150|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 9311564 (8.9M) [application/octet-stream]
Saving to: `node-v0.12.0-linux-x64.tar.gz'

100%[==================================================================================>] 9,311,564   2.19M/s   in 6.0s

2015-03-02 17:38:32 (1.49 MB/s) - `node-v0.12.0-linux-x64.tar.gz' saved [9311564/9311564]
完成後進行解壓及安裝。我選擇安裝到 /usr/local/ 內:
[root@centos ~]# cd /usr/local
[root@centos local]# tar zxvf ~/node-v0.12.0-linux-x64.tar.gz 
node-v0.12.0-linux-x64/
node-v0.12.0-linux-x64/bin/
node-v0.12.0-linux-x64/bin/node
node-v0.12.0-linux-x64/bin/npm
node-v0.12.0-linux-x64/ChangeLog
node-v0.12.0-linux-x64/share/
node-v0.12.0-linux-x64/share/man/
::    ::    ::    ::    ::
node-v0.12.0-linux-x64/lib/node_modules/npm/LICENSE
node-v0.12.0-linux-x64/lib/node_modules/npm/AUTHORS
node-v0.12.0-linux-x64/lib/node_modules/npm/.npmrc
node-v0.12.0-linux-x64/lib/node_modules/npm/.npmignore
node-v0.12.0-linux-x64/lib/node_modules/npm/cli.js
node-v0.12.0-linux-x64/LICENSE
接著是把 Node.js 加到常用路徑內:
[root@centos local]# cd /etc/profile.d/
[root@centos profile.d]# vi nodejs.sh
輸入以下內容:
pathmunge /usr/local/node-v0.12.0-linux-x64/bin/
儲存及離開後,要把 nodejs.sh 的權限修改一下:
[root@centos profile.d]# chmod +x nodejs.sh
修改完成,重啟系統:
[root@centos profile.d]# reboot