星期二, 12月 25, 2012

npm 在自訂路徑安裝全域套件

有時候系統想裝 coffee-script 但因爲不是自己的機器沒有 root,這時候可以把 npm 的安裝路徑自訂成想要的地方,再把他加到 PATH 就可以了:
npm config set prefix=$HOME/npm
echo export PATH=\$PATH:$HOME/npm/bin >> .bashrc

星期六, 12月 08, 2012

rimeime 注音查拼音

最近想來學拼音,主要是因爲我覺得手機上的注音輸入法都只是堪用而已,然後又剛好看到有所謂的 google pinyin,掛了 google 的名字就讓我很想試試看。http://code.google.com/p/rimeime/ 先是找了 google pinyin,雖然是從 android 上移植過來的但不太好用的感覺。後來就找到了 RIMEIME,基於 ibus 上但又好像是獨立的一個框架。 由於真的不太收悉拼音,希望有個反查的功能,rimeime 剛好有這個功能所以我就選了它。 rimeime 預設拼音的反查是用倉頡但我也不會,要改成注音的話,我的方法是複製一份拼音,把 reverse_lookup 的部分改成
reverse_lookup:
  dictionary: terra_pinyin
  prism: bopomofo
  enable_completion: true
  prefix: "`" 
  tips: 〔注音〕
  preedit_format:
    - 'xlit|1qaz2wsxedcrfv5tgbyhnujm8ik,9ol.0p;/-6347|ㄅㄆㄇㄈㄉㄊㄋㄌㄍㄎㄏㄐㄑㄒㄓㄔㄕㄖㄗㄘㄙㄧㄨㄩㄚㄛㄜㄝㄞ
  comment_format:
    - xform/([nl])v/$1ü/
然後 recognizer 的地方改成
recognizer:
  import_preset: default
  patterns:
    reverse_lookup: "`[a-z0-9,./;\\-]*$"
在 default.xml 裡加入自訂的方案再重新載入以後用 ` 就會是注音反查的功能囉! (這篇用拼音打了快一個小時吧...加油!)

星期四, 11月 29, 2012

awesome wm 陽春 clipboard manager

雖然說我使用滑鼠的時間真的是很少,但偶爾還是不免要從網頁上複製一些文字下來,但 X 的複製機制有時候有點令人抓狂,如果我要把一個 Textbox 的字取代掉,我不能把裡面的文字選起來按刪除,因為這樣我的 Primary buffer 裡就會被蓋掉,用 shift-ins 貼上的東西只會是我剛剛選起來刪掉的文字而已,這樣一來變成要先刪除,回到我要複製的地方重選然後再來貼上。
網路上是有不少的 clipboard manager 但是我覺得為了這個不太常用但是需要的時候才打開又太遲的功能應該是要整合到 wm 裡,所以我就寫了一個簡單的 clipboard manager,其實沒什麼 manager 的功能,只是提供三個功能:把現在的 primary buffer 存到檔案裡,選擇之前存起來的內容貼回 primary buffer 裡,跟清除暫存起來的 buffer。

code 在 github 可以下載,用法大致如下:
把 clipboard.lua 放置在 rc.lua 同個目錄,然後在 rc.lua 的開頭引入
require("clipboard")
然後在全域快捷鍵設定的地方加入下面的 code
    -- clipboard
    -- {{{
    awful.key({ modkey },            "c",     copy_to_clipboard),
    awful.key({ modkey, "Shift"   }, "c",     clear_clipboard),
    awful.key({ modkey },            "v",     clipboard_menu),
    -- }}}

我是設定 modkey(win) + c, 把 primary buffer(選取的文字)寫入 ~/.sel
modkey + shift + c 把 ~/.sel 清空
modkey + v 則會跳出一個選單把暫存的 buffer 都列出來,選擇以後就會覆蓋掉目前的 primary buffer,再按 shift + ins 即可貼上。

目前的作法蠻陽春的,換行的部份我用 #NL 取代掉才寫入,可想而知早晚會出事(?!),而且面對不是文字的 buffer 也還有待測試,不過對我來說算是夠用了啦,我會複製的東西也就是純文字的 code 而已 XDD,當然如果有意願想要改進它,也歡迎送 pull request 過來。

星期六, 11月 17, 2012

ctrlp.vim 專案開發時開檔案的好幫手

最近看到一些人開始用 Sublime Text2,所以去官網看了一下介紹,個人是沒有使用過但是那個 fuzzy search 的功能讓我蠻喜歡的。 剛好,上個禮拜在 Vim-Taiwan 中看到有人分享 ctrlp.vim 這個 plugin,雖然大部分的時候我很清楚什麼檔案放在什麼地方,但是有時候要輸入一堆路徑,即使大部分都可以 tab 補完,還是覺得有點煩人,決定來試看看這個 plugin。 這個 plugin 主要的目的就是找檔案,裝好以後按 ctrl+p 會叫出搜尋的 window,比如說我想找到的檔案路徑是
/assets/js/user.iced
那我只要輸入類似這種的路徑: as/j/u.id 就可以找到需要的檔案(當然要看有沒有其他檔案有類似的組成) 找到以後有幾種開啟方法:

  • enter: 可以在目前 window 中打開(如果已存在其他 tab 中會跳過去)
  • C-t: 會開新的 tab
  • C-v: 把目前的 window 做垂直切割開啟
其他一些指令可以參考 :help ctrlp-mappings 剛裝完 ctrlp 的時候可能會覺得他啟動有點慢,在 OSX/Linux 底下的話可以設定用 git ls-files 或 find 來做檔案列表的建立,速度會有明顯的改善,在 ~/.vimrc 裡面加入
  let g:ctrlp_user_command = {
    \ 'types': {
      \ 1: ['.git', 'cd %s && git ls-files -c -o --exclude-standard'],
      \ 2: ['.hg', 'hg --cwd %s locate -I .'],
      \ },
    \ 'fallback': 'find %s -type f'
    \ }
*2014 09/11 Update: 加入 --exclude-standard 這是從 :help ctrlp_user_command 複製下來的範例把 git ls-files 改成 git ls-files -c -o,這樣才能一併搜尋到還沒被 cache 的檔案。
----
另外有一個 plugin 叫做 unite.vim,似乎也不錯,自訂性更高,除了該有的檔案, MRU, Buffer 搜尋,使用者還可以自訂搜尋的來源,如何篩選,以及如何打開。換句話說,你可以搜任何存在/不存在的東西,讓我想到也許可以做出類似 Dash 的東西(一開始看到 dash 是想說可能要做成一種輸入法應該蠻酷的)

Node.js mysql connection pool

Node.js 中,我常用的 mysql module 大概是 node-mysql 了吧,但是因為想要盡量避免太多連線的建立,以前會在 module 中戶傳連線的物件,但又發現會有 timeout 的問題,這部分 node-mysql 的處理方式並不是很好需要重建連線,但是重建連線以後除非又要再把連線的物件傳到每個 module 不然無法使用。 另一方面,node-mysql 本身的 query 是 serial 的,人一多的時候若整個 application 只存在一個連線其實是不夠的,但以我以前的做法,多個連線會變成非常複雜。 於是我翻到了 node-mysql 首頁的最下面 TODO:

  • Prepared statements
  • setTimeout() for Connection / Query
  • connection pooling
  • ...
想一想,做一個共用的 connection pool 的話應該就能免去一些不必要的連線建立,同時也能在不同 module 中共用連線,又能達到多個 query 同時處理(用多個 connection 達到)的效果。以下是我透過 generic-pool 做出來的 mysql_pool.iced
mysql = require('mysql')
config = require __dirname + '/../config'

poolFactory = require 'generic-pool'
module.exports = poolFactory.Pool {
        name: 'mysql'
        create: (callback)->
                conn = mysql.createConnection {
                        host : 'localhost',
                        user :config.dbuser,
                        password : config.dbpass,
                }
                conn.query 'use ' + config.dbname
                callback(null, conn)
                return
        destroy: (conn)->
                conn.end()
                return
        max: 10
        idleTimeoutMillis: 30000
}
然後將這個 module 放到 node_modules 底下(方便各個 module require),因為 node.js 載入 module 的特性,同個位置的 module (基本上)會被 cache 起來,所以所有用到 pool 的地方都會從同一個 connection pool 來取得/試放連線,達到共用連線的目的。 這邊我建完 connection 先下了 use 的 query 再回傳,理論上應該要等 query 返回再回傳,但因為前面講過 node-mysql 是 serial 的所以沒關係(但以後可能會需要改就是了)..

星期六, 11月 03, 2012

LinearLayout & weight

個人在 Android 中最常用的就是 LinearLayout 了,可能是因為以前寫 gtk 的關係吧,對這個方式覺得比較直覺。

LinearLayout 中,有時後會用到 weight 來讓版面達到滿版的效果。但是有時候 child view 的 weight 越大,暫的空間越大,有時候又是相反。為了釐清只好找了 LinearLayout 的 Source 來研究:(widget/LinearLayout.java)
1147 // Either expand children with weight to take up available space or                                        
1148 // shrink them if they extend beyond our current bounds
1149 int delta = widthSize - mTotalLength;
1150 if (delta != 0 && totalWeight > 0.0f) {
1151     float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
...
...
1169 float childExtra = lp.weight;
1170 if (childExtra > 0) {
1171     // Child said it could absorb extra space -- give him his share
1172     int share = (int) (childExtra * delta / weightSum);
1173     weightSum -= childExtra;
1174     delta -= share;
上面這兩段基本上就是原因,當所有 child view 算出來的高度/寬度小於自己的高/寬的時候,delta 是正的,會依照 weight 的比例分給這些 child view 讓 weight > 0 的 child view 來吸收多出來的空間。反之,若寬度超過預算,也是由這些 child view 縮小吸收,所以會才會有兩種不同的狀況。

星期一, 4月 30, 2012

ContentObserver, 觀察 ContentProvider 的更新

當我們需要對 ContentProvider 的內容更新做出即時的反應,可以用 ContentObserver 來達到監控的效果。

用起來還蠻簡單的,實做一個 ContentObserver 以後,用 ContentResolver 來註冊一個 observer。
private class MyObserver extends ContentObserver {
 public MyObserver(Handler handler)
 {
  super(handler);
 }

 /**
  * {@inheritDoc}
  * @see ContentObserver#onChange(boolean)
  */
 public void onChange(boolean selfChange)
 {
  Log.d("MyObserver", String.format("onChange: %s", selfChange?"1":"0"));
 }
}
ContentResolver cr = getContentResolver();
cr.registerContentObserver(Events.CONTENT_URI, false,
 new MyObserver(new Handler()));

Observer 中除了 constructor,我們只要 override onChange 就可以了。當目標內容有改變並通知時,onChange 就會被呼叫。

有個問題是,我不知道為什麼我實際上在使用的時候,每次資料有變更 onChange 都會被呼叫兩次。目前因為資料的變動週期不短,所以我對每個 onChange 都做出反應不會有什麼問題,但是如果遇到資料更新頻率很高的應用時,也許可以配合 PendingIntent (FLAG_UPDATE_CURRENT, FLAG_NO_CREATE) 來做延後的合併處理。

return values of Android Service onStartCommand

實做 Service 時,最重要的應該就是 onStartCommand 的實作了吧,當其他 context 呼叫 startService 時,這個函數都會被呼叫。
onStartCommand 的回傳值可以有 4 種,分別是 START_NOT_STICKYSTART_REDELIVER_INTENTSTART_STICKY 跟 START_STICKY_COMPATIBILITY

若回傳 START_NOT_STICKY,表示這個服務不需要一直存留,當 Service 因其他程序需要記憶體砍掉以後,除非有另一次的 startService 呼叫,不然系統不會幫你重新執行 Service。

回傳 START_REDELIVER_INTENT 時,若 Service 被砍掉以後,系統會嘗試使用當初呼叫 startService 的 intent 來重新啟動這個 Service。

START_STICKY 跟 START_REDELIVER_INTENT 類似,但並不會保留上次用來開啟服務的 intent,也就是說如果 service 被系統砍掉後重開,被砍掉到重新啟動中間如果沒有 startService 來傳遞新的 intent,送進來的 intent 就有可能為 null,所以如果在 onStartCommand 裡面需要讀取 intent 裡的 bundle 一定要先確認 intent 是否為 null。

最後一個 START_STICKY_COMPATIBILITY,跟 START_STICKY 一樣,但根據官方的文件,使用這個 return code 時當 service 被砍掉後系統不一定會再呼叫一次 onStartCommand。也就是說如果是 sticky 的話系統一定會再 reschedule 一個 onStartCommand 嗎.?

Refs:
http://developer.android.com/reference/android/app/Service.html

星期五, 4月 27, 2012

query content provider with LIMIT

在 query ContentProvider 的時候要用 limit 的話,直接加在 sortOrder 後面就可以了。

Cursor c = cr.query(
 Uri.parse(uri),
 INS_PROJECTION,
 Instances.CALENDAR_ID + "=" + cal_id,
 null,
 "limit 1"
)

星期一, 4月 16, 2012

SimpleCursorAdapter & _id

基本上 Android 裡面的 Adapter 的工作就是一個將 "Item" (包括 data, meta-data) 包裝成 item view 的類別.
ListView 在 render 的時候會向 Adapter 要一個一個 view 用來表示每個資料.
SimpleCursorAdapter 是把 Cursor 包裝起來, 用來產生 listview 需要顯示的一個個項目。值得一提的是,Cursor 代表的資料需要一個 "_id" 的欄位, 用來當作 onListItemClick 的識別碼,也就是第三個參數。
所以在做查詢的時候都要 Projection 記得選 _ID,不然會噴 IllegalArgumentException。

Ref: