2013年1月21日 星期一

[Ubuntu] 安裝PostgreSQL9.1步驟

雖然一開始Ubuntu就安裝了postgresql,但是在搞不清楚狀況的一開始我還是把它給砍了= =
然後再重裝一遍:
sudo apt-get install -y postgresql-9.1 postgresql-client-9.1 postgresql-contrib-9.1

相關的執行檔會被放在這個資料夾:
/usr/lib/postgresql/9.1/bin

在安裝時一直遇到找不到放置PGDATA的錯誤訊息,所以我們要在9.1資料夾下創建一個"data"資料夾,並更改擁有者為postgres:
mkdir /usr/lib/postgresql/9.1/data
chown postgres /usr/lib/postgresql/9.1/data
此時再執行su postgres,切換為postgres身分,在/usr/lib/postgresql/9.1/bin的路徑下執行:
./initdb -D mkdir ../data
會出現以下的訊息:
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale en_US.UTF-8.
The default database encoding has accordingly been set to UTF8.
The default text search configuration will be set to "english".

fixing permissions on existing directory ../data ... ok
creating subdirectories ... ok
selecting default max_connections ... 100
selecting default shared_buffers ... 24MB
creating configuration files ... ok
creating template1 database in ../data/base/1 ... ok
initializing pg_authid ... ok
initializing dependencies ... ok
creating system views ... ok
loading system objects' descriptions ... ok
creating collations ... ok
creating conversions ... ok
creating dictionaries ... ok
setting privileges on built-in objects ... ok
creating information schema ... ok
loading PL/pgSQL server-side language ... ok
vacuuming database template1 ... ok
copying template1 to template0 ... ok
copying template1 to postgres ... ok

WARNING: enabling "trust" authentication for local connections
You can change this by editing pg_hba.conf or using the -A option the
next time you run initdb.

Success. You can now start the database server using:

    ./postgres -D ../data
or
    ./pg_ctl -D ../data -l logfile start


接著我們就可以執行
./postgres -D ../data
或
./pg_ctl -D ../data -l logfile start
來啟動服務了。

Postgresql這時候已經可以接受連線,我們來執行createdb指令來新增一個資料庫:
postgres@li440-29:/usr/lib/postgresql/9.1/bin$ createdb test

然後執行psql -d test來進入console,如果直接執行psql是會直接進入預設的postgres資料庫的,快樂的打指令之前請先確認一下所在的DB。

而pg_ctl是用來對postgres做操作的控制台,可以執行"pg_ctl --help"來查看它的功能,這邊先列出一部分:
Usage:
  pg_ctl init[db]               [-D DATADIR] [-s] [-o "OPTIONS"]
  pg_ctl start   [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o "OPTIONS"]
  pg_ctl stop    [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]
  pg_ctl restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]
                 [-o "OPTIONS"]
  pg_ctl reload  [-D DATADIR] [-s]
  pg_ctl status  [-D DATADIR]
  pg_ctl promote [-D DATADIR] [-s]
  pg_ctl kill    SIGNALNAME PID

此時執行netstat -ap應該能看到postgres正在listen

但因為每次啟動PG都要輸入-D參數實在太麻煩,我們是先將PGDATA加入環境變數中,打開~/.bashrc,在最後一行加上:
export PGDATA=/usr/lib/postgresql/9.1/data

後記

在重啟服務時,常常會有失敗的情況,這時只要切換終止模式即可:
./pg_ctl -D ../data restart -m fast

2012年12月5日 星期三

[觀察] 關於旺中...你不該、也沒有理由生氣


最近聽到有人在批儒家思想,不由得打了個冷顫,不由得把這樣的說法與文革聯想在一起

誰才是正義?

一兩個學生說:「媒體不對,不該搞壟斷」,其他學生想了想也對,反正社會輿論站我這邊,同學們都支持我。

「媒體說的不對,我們才代表真理!」

這樣的激情下,大家一窩蜂擁護學生,學生更加有恃無恐。
科技很先進,FB、youtube上瘋狂流傳著懶人包、剪接影片,把大家帶到情緒的高峰,大家像著魔似的在鍵盤前敲打"民主已死"。但怎麼不想想這樣的氛圍或許是另一種洗腦?這樣的批鬥是另一種暴力?

社會本來就是這樣,任何一方都不應該被偏袒,每個人都應該被尊重。口口聲聲"社會正義",你們落實了嗎?還是說,你就代表著正義?


面對巨獸,失控就是你的解決方案?

去年到大陸的時候參觀北京的孫中山紀念館,聽導覽人員不斷的稱讚我們的國父,我諷刺的問說「既然你們覺得他這麼偉大,怎麼不把他印在你們鈔票上?」
只見那位四五十歲的導覽人員臉色尷尬,解釋說當時有些"很左"的東西,嘰哩咕嚕說不上來,我就不問了。
大陸很多地方還是很糟,還是很多陋習。但是他們知道以前"很左"的東西害了他們,文化大革命毀了太多智慧的結晶。我們很幸運,沒有經過那樣的時代,但不代表我們一定要經歷相同的錯誤才能知道痛。

再說個故事,另一次在大陸的自由行,參觀青島的總督府時,看見一個銅製的人臉壁飾,但是臉是凹下去的。同行的北大學生恨恨的說,"就是以前文革的瘋子搞的"。諷刺的是,當時北大學生在文化大革命扮演了多重要的角色和推手啊....

對,一個很龐大的權力體早就把魔爪伸過來了,我怎麼不明白?我曾經在上海跟一個有我兩倍塊頭的山東商人爭論台灣主權...

我說 「你們再怎麼大,也得把我們打掛了再說」

他說 「不需要,我們就快把你們買過來了,總統大選我們就"投資"了不少」

那個商人沒多大,大概只有半個蔡衍明,而這樣的人,在大陸多如牛毛。

當人家擺明了要用算不清的錢和權力來跟你硬碰時,台灣的年輕人卻先被自己的憤怒給吞噬。

你們說台灣的媒體沒有良知,猙獰怒吼的同時,幾間獨立媒體正默默著耕耘著,希望大家能注意到他們的努力和苦守的立場。很想問問同學們,除了抗議以外,有沒有真的去關心過這些媒體界最後的良知。還是一味肖想人家花了大錢買下的媒體事業一夕之間轉變方向就為了順您的意?


這樣的媒體很糟,誰養出來的?

當中時不再藍、自由不再綠、蘋果日報不再腥羶色,最後就會"不好看"、"不有趣"而被你們遺忘.....


就像那些獨立媒體一樣


想問,
究竟是台灣的觀眾選擇了這種水準的媒體,還是這些媒體巨獸就這麼憑空出現,搶走了你我閱知的權力?



2012年11月29日 星期四

[宅] 令我老淚縱橫的deferred物件

昨天參加完Ruby tuesday後不只解決了困擾已久的環境問題,還在聊天中得知了一個超好用的東西,就是jquery中的deferred物件.....雖然我LAG了很久但秉持著活到老學到老的精神,我還是要給他不知廉恥的寫下去。


事情是從jquery 1.5版開始的....


"老實說,我並不知道1.5版發生了什麼事情,但當我回過神來的時候,jquery的ajax函式已經開始回傳deferred物件了...."


jquery.Deferred(),或稱作$.Deferred(),是一個function,執行後會回傳一個Deferred物件:
Deferred: object
-->resolve: function
-->resolveWith: function
-->reject: function
-->rejectWith: function
-->notify: function
-->notifyWith: function
-->state: function
-->always: function
-->then: function
-->promise: function
-->pipe: function
-->done: function
-->fail: function
-->progress: function

這個物件和Ajax所回傳的object長得不太一樣(以下是get error的狀態):
deferred: object
-->abort: function
-->always: function
-->complete: function
-->done: function
-->error: function
-->fail: function
-->getAllResponseHeaders: function
-->getResponseHeader: function
-->pipe: function
-->progress: function
-->promise: function
-->readyState: 0
-->responseText: ""
-->setRequestHeader: function
-->state: function
-->status: 0
-->statusCode: function
-->statusText: "error"
-->success: function
-->then: function
黑字的部分都是deferred物件的function,但是明顯看得出比原生的deferred物件少了許多,我們姑且稱這是"閹割版"的deferred object,後面會再提到閹割版的由來。

用Ajax的結果來執行callback

Ajax回傳的物件中的"done"、"fail"函式,可以依成功與否來執行傳入的callback function,而很方便的是,這些函式是可以chain起來的。
$.get("http://google.com/").done(function(){alert("YES!!!");}).fail(function(){alert("NO!!!");});
這樣一來,成功的話就會alert "YES",失敗就alert "NO",而上面的寫法也可以用then()來做簡化:
$.get("http://google.com/").then(
    function(){alert("YES!!!");}, //成功時的callback
    function(){alert("NO!!!");} //失敗時的callback
)
但其實Ajax中本來就可以傳入"success"和"error"的callback function,所以光是上面的應用除了讓程式碼的可讀性變得更高也更好寫(當需要error callback時不用刻意改寫成ajax,仍舊可以使用get, getJSON, post)以外,似乎還是有點弱。
接著我們要介紹的是能讓deferred發揮得更強大的$.when()函式


用$.when( )解決"一堆"非同步AJAX的相依性問題

很多時候我們的程式碼會需要依賴數個AJAX回傳的資料來進行運算,以往我們可能只能用下列兩種解法:

  1. 改寫成同步AJAX,也就是一個一個接著執行
  2. 設一個值等於要AJAX次數的counter,每完成一個AJAX就減一,直到counter歸零就執行相依性的函式

但是第一種solution效率太差,第二種又太醜。這時我們就可以使用$.when( )
一般時候使用$.when()跟直接使用deferred物件是差不多的,因為$.when()也是回傳一個deferred物件:
var def = $.when($.ajax("test.html")); //deferred物件
$.when()真正強悍的地方在於能將數個AJAX的結果包成一個deferred物件,只要有一個失敗,那這個deffered的狀態就是"error",這樣講很抽象,我們直接看例子:
$.when( $.get("test1.html"), 
        $.post("test2.php"), 
        $.get("test3.html")
).then( doneCallback,
        failCallback    )
上面的三個AJAX只要有一個失敗了就會觸發errorCallback,三個都成功才會觸發successCallback。
這樣的寫法乾淨許多,可讀性也高。
延伸:AJAX的success callback與deferred的done callback 的執行順序
javascript是單執行緒的語言,但是常常會因為AJAX或wait這些常用的funciton讓初學者有所誤會。如果是非同步的情形,AJAX在等待遠端回覆的過程中,會直接往下執行,直到遠端回應了才會觸發success或error callback。
但是在同一時間javascript是只會做一件事的。所以在上面提到的例子中,如果數個AJAX有傳入success callback,當成功接收到respond時,AJAX會先呼叫success callback,就看哪個AJAX先收到respond而已。而因為javascript單執行緒的特性,在沒有ajax,timer,alert之類的情形下他會完完整整的把一個success callback執行完才執行下一個AJAX的callback。
所以上面的例子中,將會是三個AJAX的callback在不可預期的順序下執行完畢後,才會由$.when()回傳一個狀態為成功的deferred來觸發done callback function。不必擔心done callback有可能比AJAX的callback先被處理。


deferred不只能用在AJAX

如果其他函式也是非同步且具有相依性的需求,例如wait(),我們也可以利用deferred的設計思維來做相依性的處理。但是因為wait()並不回傳deferred物件,所以我們要再把這些函式包起來變成一個回傳deferred物件的函式。舉個例子:(轉載自阮一峰大大的部落格)
var wait = function(dtd){
  var tasks = function(){
    alert("执行完毕!");
    dtd.resolve(); // 改变Deferred对象的执行状态
  };

  setTimeout(tasks,5000);
  return dtd.promise(); // 返回promise对象
};
var d = wait(dtd); // 新建一个d对象,改为对这个对象进行操作
$.when(d)
.done(function(){ alert("哈哈,成功了!"); })
.fail(function(){ alert("出错啦!"); });
d.resolve(); // 此时,这个语句是无效的
這個例子裡有兩個東西要注意,一個是"resolve()"、一個是"promise()"。
resolve()可以將deferred物件改為"成功"狀態並觸發done callback,相反的就是reject了。
而promise()能將deferred物件"閹割"成文章最前面所呈現的樣子,這樣的物件沒有辦法再用resolve或reject來更改狀態,顧名思義就是"承諾"(promise)


2012年11月28日 星期三

[宅] 在mac上的pg gem設定

在為我的rails專案安裝pg gem時發生了一直找不到pqlib,為了解決這個問題花了超多時間,後來發現原來mountain lion預設安裝postgres,依照這篇文章的教學安裝了gcc(https://github.com/kennethreitz/osx-gcc-installer),結果有找到pqlib,卻在下一步失敗,原因是找不到Development資料夾。

後來依照這篇修改xcode路徑:
sudo xcode-select -switch /Applications/Xcode.app
又往前邁進了一步,但仍舊是失敗,原因似乎是make的時候出了問題。

後來在Rails meet-up請人幫我檢查可能的原因,發現有可能是因為我用了home brew來安裝ruby的關係,但是又沒做好相關設定,所以gem在找libary的時候會找錯路徑。
所以當下果斷移除brew所安裝的ruby,再安裝rvm,而rvm安裝的過程中就會幫你安裝最新版的ruby。結果....
發現我已經裝了rvm啦!(什麼時候裝的Orz)
但是用"rvm list"這個指令來檢查發現rvm下並沒有安裝任何ruby,這個原因也搞不太清楚,但是反正不難解決就不去探究,於是怒裝ruby 1.9.3:
rvm install 1.9.3

正要bundle install時,原本我是要用sudo身分來安裝的,但是大大說沒有必要,甚至用sudo身分安裝的gem可能會無法在一般身分下執行。
怒"邦豆"一發:
bundle install
終於成功了,宅宅終於可以在很潮的mac上開發rails了~:.゚ヽ(´∀`。)ノ゚.:


心得

安裝ruby最好還是用rvm,其他與開發環境較無關的套件再用brew來安裝


後記

為了貪圖方便,在開發時我是使用postgres.app,啟動方式很簡單,只要開啟程式,本機端就有資料庫了。它會預設以使用者的名字創建一個user和資料庫,在終端機輸入:"psql"就會直接登入這個資料庫。
如果出現訊息說找不到server,可以先用"which psql"來檢查psql所在位置,如果之前就已經安裝了原生的postgres,很有可能回傳"/usr/local/bin/",這樣代表我們並不是用app所提供的bin。
這時就要更改路徑,app自己的psql才會找到app所啟動的server:
PATH="/Applications/Postgres.app/Contents/MacOS/bin:$PATH"
要建立自己的資料庫可以在終端機輸入:
createdb <"資料庫名稱">

然後在rake db:migrate時發現還是有問題,改為這樣執行:
bundle exec rake db:migrate



2012年11月27日 星期二

[宅] Grails的Scaffolding實作

在建立一個Grails專案後會發現,我們找得到controller和view,但卻找不到model。原來在Grails中,MVC的model是對應到grails特有的domain class。

domain-class - MVC中的Model

開發一個mvc應用,通常要先從model下手,現在我們就來製作一個model,在終端機輸入:
grails create-domain-class user
會產生User.groovy:
package <project_name>

class User {

    static constraints = {
    }
}
在Rails中產生model時,可以在指令後面加上一串參數來直接定義這個class的attributes。
Grails沒有這樣的機制,所以要在產生class之後手動進行
package gbookshelf

class User {
 String uid
 String name
 String email
    static constraints = {
    }
}
constraints這個參數是用來做資料驗證的,可以限制存入資料的條件,例如:
static constraints = {
        uid(size: 1..20, blank: false, unique: true)
        name(size: 0..80, nullable: false)
        email(maxSize: 40, blank: false, nullable: true)
}


Scaffold - 好用的crud"速成"工具

在Grails中,scaffold分成動態及靜態兩種產生方式,先講動態產生的方法。在domain class中寫入所需的attributes之後,輸入:
grails generate-views .User
這行程式碼的會自動幫你產生Scaffold的"views",有"_form.gsp", "edit.gsp", "create.gsp", "list.gsp", "show.gsp" ,這些都是CRUD操作所會用到的。

到了這裡程式還是跑不起來,因為我們還沒有controller,用這個指令產生一個controller:
grails create-controller user
這時候你會拿到一個很乾淨的UserController.groovy和UserControllerTests.groovy。

雖然這時我們有了一堆views,但是因為這個很乾淨的controller根本還沒定義action所以就算啟動了這個app也是找不到"user/list"的。

動態scaffold

從這裡開始就是"動態scaffold"和"靜態scaffold"不一樣的地方了:
在UserController中加入這一行程式碼,啟動scaffold功能:
static scaffold = true
變成
class UserController {
    static scaffold = true
}
這行程式碼會自動幫你搞定以下這些action:
  • list
  • show
  • edit
  • delete
  • create
  • save
  • update

靜態Scaffold

但是這樣的機制雖然方便,但卻像個黑盒子一樣,我們不清楚裡面的流程,更無從加入邏輯。所以我們可以用這個指令來產生controller:
grails generate-controller <project_name>.User
產生出來的controller會長這樣:
package <project_name>

import org.springframework.dao.DataIntegrityViolationException

class UserController {

    static allowedMethods = [save: "POST", update: "POST", delete: "POST"]

    def index() {
        redirect(action: "list", params: params)
    }

    def list(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        [userInstanceList: User.list(params), userInstanceTotal: User.count()]
    }

    def create() {
        [userInstance: new User(params)]
    }

    def save() {
        def userInstance = new User(params)
        if (!userInstance.save(flush: true)) {
            render(view: "create", model: [userInstance: userInstance])
            return
        }

        flash.message = message(code: 'default.created.message', args: [message(code: 'user.label', default: 'User'), userInstance.id])
        redirect(action: "show", id: userInstance.id)
    }

    def show(Long id) {
        def userInstance = User.get(id)
        if (!userInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "list")
            return
        }

        [userInstance: userInstance]
    }

    def edit(Long id) {
        def userInstance = User.get(id)
        if (!userInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "list")
            return
        }

        [userInstance: userInstance]
    }

    def update(Long id, Long version) {
        def userInstance = User.get(id)
        if (!userInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "list")
            return
        }

        if (version != null) {
            if (userInstance.version > version) {
                userInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
                          [message(code: 'user.label', default: 'User')] as Object[],
                          "Another user has updated this User while you were editing")
                render(view: "edit", model: [userInstance: userInstance])
                return
            }
        }

        userInstance.properties = params

        if (!userInstance.save(flush: true)) {
            render(view: "edit", model: [userInstance: userInstance])
            return
        }

        flash.message = message(code: 'default.updated.message', args: [message(code: 'user.label', default: 'User'), userInstance.id])
        redirect(action: "show", id: userInstance.id)
    }

    def delete(Long id) {
        def userInstance = User.get(id)
        if (!userInstance) {
            flash.message = message(code: 'default.not.found.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "list")
            return
        }

        try {
            userInstance.delete(flush: true)
            flash.message = message(code: 'default.deleted.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "list")
        }
        catch (DataIntegrityViolationException e) {
            flash.message = message(code: 'default.not.deleted.message', args: [message(code: 'user.label', default: 'User'), id])
            redirect(action: "show", id: id)
        }
    }
}

這樣整個邏輯一目了然,也可以加入自己的邏輯了。
或是我們可以用這個指令,一步產生Scaffold的"view", "controller", "test unit"
grails generate-all gbookshelf.User


小結

在剛剛的範例中會看到generate-controller和create-controller兩種指令,差別在於"create-controller"只會產生一個空白的controller,而"generate-controller"必須依據一個現有的domain class來產生一個scaffold的controller。而"generate-all"、"generate-controller"、"generate-views"都是用來產生scaffold所需檔案的指令。
要注意的是,當在使用這些scaffold指令時,必須在指令後加上要依賴的"project_name.domain_class",例如:
grails generate-all myApp.User
而"grails generate-all User"是會出現錯誤的。

[JS] function物件的call()&apply()

在javascript中,除了直接以function加上"()"來執行函式外,還可以呼叫function的call()或apply()來執行,但是"foo()"、"foo.call()"和"foo.apply()"其實是有差別的,接下來我們就來看看call()的用法:

function.prototype.call()

在browser上運行的javascript,其global scope是window,但當在不同地方呼叫global的function時,我也希望這個funciton能依據呼叫的所在而使用當時的scope,這時我們就能使用call。
以下是一段簡單的程式碼:
scope="window";
foo = function(){console.log(this.scope);};
cat={
    scope:"cat",
    foo:function(){console.log(this.scope);},
    bar:function(cb){cb();}
};

foo(); //"window"
cat.foo(); //"cat"

cat.bar(foo); //"window"
foo.call(cat); //"cat"
從這段簡短的程式碼中就可以看出call可以改變韓式執行的scope,這個功能常用在事件處理上。


function.prototype.apply()


apply()的功能跟call很像,差別在於參數的傳遞方式不同:
call(valueForThis, arg1, arg2, ...) //一個一個指定參數
apply(valueForThis, arrayOfArgs) //將參數放到陣列後傳入
真的說起來,apply()還比call()早出現(前者v1.2,後者v1.5)



2012年11月23日 星期五

[潮] MAC必安裝,好用開發工具

"哇喔,自從我用了MAC,每天都潮到會滴水呢~" :.゚ヽ(´∀`。)ノ゚


最近為了親自體驗一下大家口口聲聲"在MAC底下開發真的比較順"的真情告白,我也忍痛敗了一台最入門的Mac book pro,再自己買16G的記憶體換上(原廠記憶體升級的cp直實在太低)。目前為止除了覺得觸控版相當好用和感覺自己帥了一點以外好像沒有其他太大感受,可能再用一陣子會比較有感覺吧。
最近裝了一些開發者必裝的套件和軟體,在此記錄一下:

HomeBrew - for MAC的套件管理工具

這個工具類似Ubuntu的apt-get,可以用來安裝、移除軟體,安裝起來不難,官網有教:
http://mxcl.github.com/homebrew/
裝起來後順便用它來裝個wget和git吧~
brew install wget
brew install git



zsh & oh_my_zsh


跟bash一樣都是shell,雖然mac本來就有裝zsh,但是開啟終端機時預設是使用bash,只要換過來就好了:
chsh -s /bin/zsh
然後重開終端機就好(大部分程式直接按叉叉是不會真的被關掉的,要用command+Q)

但光是zsh好像沒有比bash強到哪裡,聽說真正好用的是oh_my_zsh,但是我還沒玩透,或許改天能分享,安裝請看:
https://github.com/robbyrussell/oh-my-zsh


Dash - document和snippet的管理軟體

反正就是可以事先下載文件然後offline查詢,是有方便一些。但是真正強大的是他的snippet功能,可以事先寫好程式碼並命名,在筆記本打那個snippet的名字就會自動貼上程式碼了。但是不知道為什麼在sublime2中不會這麼做Orz


Sublime 2 - 呃...很潮的文字編輯器

其實也沒有很潮,但是他支援了不少plugin如grails和....不知道了自己查。
他也有snippet的功能,和很多讓你coding起來會快一點點的功能,這東西我也一樣沒玩熟,陣痛期嘛....