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起來會快一點點的功能,這東西我也一樣沒玩熟,陣痛期嘛....

2012年11月22日 星期四

[宅] Grails起步走

這兩天一直很想寫網誌,這幾天都沒更新網誌代表自己最近實在很偷懶....
剛好最近為了尋找公司系統的替代方案,開始接觸Grails - 以Groovy動態語言來設計的仿ROR Framework,簡單來說就是"Groovy on rails"。

安裝Grails環境

1. 首先到官網下載Grails:http://grails.org/Download
2. 將下載的壓縮檔解壓縮到你認為適合安置他的地方
因為我現在是用Win7來測試他,所以接下來是Win7下的環境設定
3. 在控制台內找到"系統及安全性">"系統">左側的"進階系統設定",點進去後在"進階分頁"下點選"環境變數",接著在系統變數下找到"path",並且編輯他。
4. 在path後面加上你放置grails/bin的路徑,例如:C:\grails-2.1.1\bin (記得和前面的路徑用分號";"隔開)

因為Groovy其實還是靠JVM來執行的,所以Grails預設會從JAVA_HOME這個環境變數去找你安裝JAVA的位置,因此我們還要新增一個"JAVA_HOME"環境變數:
5. 在剛剛修改path的地方(系統變數)按"新增"
6. 在"變數名稱"欄位打上"JAVA_HOME",在"變數值"欄位輸入你安裝JAVA的路徑

這樣就可以了。
接下來打開終端機,輸入"grails -version",應該就會看到目前的安裝版本,代表你安裝成功囉!


建立第一個Grails APP - "Hello world"

grails create-app helloworld
這時候Grails會在你所在的目錄下新增一個"helloworld"資料夾,進入"helloworld"後,啟動這個app:
cd helloworld
grails run-app
等到出現"| Server running. Browse to http://localhost:8080/helloworld"的訊息後就可以連到剛剛的網址看到預設的首頁囉。

2012年11月17日 星期六

[宅] 在Ubuntu安裝PostgreSQL

之前的環境是在windows,現在開始要慢慢搬到Ubuntu上,但是在安裝的過程中碰到了一點麻煩,在這邊做一個筆記,希望有相同問題的人或自己以後能更順利。

Install PostgreSQL 

安裝的過程很簡單沒有什麼問題,但是不知道為什麼9.1版在我的電腦跑不起來,所以改裝8.4
sudo apt-get install postgresql-8.4


啟動PostgreSQL

sudo /etc/init.d/postgresql-8.4 start
如果不行的話把版本去掉
sudo /etc/init.d/postgresql start

其他postgredsql操作:
sudo /etc/init.d/postgresql stop
sudo /etc/init.d/postgresql restart
sudo /etc/init.d/postgresql reload


設定database

對於postgresql的相關操作都是在psql這個postgresql的shell底下做的,但是一般使用者的權限不能執行他,就算你加了sudo也是一樣,所以這時候我們要切換到postgres在安裝過程中為我們建立的使用者"postgres":
sudo su postgres
接著我們就會看到shell的輸入欄位變成這樣:postgres@yourhost:
然後就可以開心的進入psql囉~
首先我們先來看看目前的使用者有誰,輸入"\du",會出現以下的畫面:

首先就是修改postgre這個superuser的密碼
ALTER USER postgres WITH PASSWORD ' <***password***> ';

要注意的是,在psql內下的SQL指令的結尾,要加上分號";"後才會執行,所以你也可以分成好幾行來打,最後一行加上分號再按ENTER就會執行了。
postgres=# SELECT *
postgres-# FROM BOOKS;


重要SQL指令

到了這邊大概都沒啥問題了,只剩一些Database和table的設定罷了。


  • 建立資料庫 CREATE DATABASE <dbname>
  • 建立資料表 CREATE TABLE <tablename>(COLUMN …)
  • 刪除資料庫 DROP DATABASE <dbname>
  • 刪除資料表 DROP TABLE <tablename>
  • 修改資料表 ALTER TABLE <tablename> …
  • 修改使用者 ALTER USER <user> WITH …


要查詢更細的設定只要在"\h"之後加上指令即可

2012年11月14日 星期三

[宅] javascript筆記 - window.location類別

window.location是個非常好用的物件,除了可以跳轉外,他還提供了很多讀取所在網址的相關資訊。我們先來看看"http://cat-son.blogspot.tw?foo=bar#a"這個頁面的window.location的attributes:

assign: function () { [native code] } //跳轉
hash: "#a" //取得錨點
host: "cat-son.blogspot.tw"
hostname: "cat-son.blogspot.tw"
href: "http://cat-son.blogspot.tw/#a"
origin: "http://cat-son.blogspot.tw"
pathname: "/"
port: ""
protocol: "http:"
reload: function () { [native code] }
replace: function () { [native code] }
search: "?foo=bar" //網址後的query值>

頁面跳轉

可以用以下幾種方法直接修改window.location的值:
window.location="http://example.com";
window.location.href="http://example.com";
location="http://example.com";
location.href="http://example.com";
或是使用window.location所提供的function來跳轉:
window.location.assign("http://example.com");
window.location.replace("http://example.com");
assign()和replace()效果差不多,差別在於replace是置換掉現在的document,不會留下被置換掉的頁面紀錄,所以當你按下"上一頁"時回到的其實是你"上上個"看到的頁面....
這樣講一定聽不懂,舉個例來說好了:

當我從A頁面逛到B頁面後,在B頁面執行了location.replace(C頁面),會把當前的document從B換成C,所以當我點上一頁時就是回到A。示意:
A-->B
↓
location.replace(C)
↓
A-->C
而使用location.assign則會是這樣的情況:
A-->B
↓
location.assign(C)
↓
A-->B-->C
所以在C頁面點上一頁會回到B而不是A


取得資訊

很多時候我們使用get這種方式來傳遞資料時,傳遞的資料會出現在網址後端,這時我們就可以從window.location.search這個字串拿到query值。例如在"http://example.com/?foo=bar"的頁面下,我們的window.location.search就會是"?foo=bar"
window.location.hash則是achor的值,也就是錨點。
但要注意的是,網址中只要在"#"之後的字串都會被視為hash,所以當你要同時傳遞anchor和query時,務必要把anchor放在最後面,例如:
"http://cat-son.blogspot.tw?foo=bar#a"


重讀頁面

使用window.location.reload(forceGet)
forceGet是個布林值,預設為false,代表只要從cache中重新載入就好,不需要再向server做http request;反之為true時,代表一定要再向server重新要資料。
這個特性還滿重要的,可以避免重讀時拿到舊資料,同樣的目的可以透過加上隨機的query值來達成。


小補充

關於document.location和window.location的差別:
window.location is read/write on all compliant browsers.
document.location is read-only in Internet Explorer (at least), but read/write in Gecko-based browsers (Firefox, SeaMonkey).




2012年11月10日 星期六

[宅] node.js KO Hackthon初體驗

現在是晚上將近11點,小弟的TEAM還在做很廢的功能.....XD
好啦,不說廢話認真作筆記:

MongoDB

之前一直沒有好好鑽研他,直到今天實在是沒法度了(MySQL哭哭),臨時抱佛腳開始使用MongoDB(這個決定好像也是晚上10點決定的Orz)。

下次參加Hackthon要先做功課啊啊啊啊

MongoDB其實是不太需要"建立"所謂的資料庫的,當你需要一個新的資料庫,概念上像是javascript中創建一個object一樣。預設好像是使用test,讓我們來新創一個db object:
> use myDB
switched to db myDB
來看看現在是使用哪個DB呢
> db
myDB
good~然後我們要連到DB時大概像是這樣:
mongodb://usr:password@localhost/myDB

讓我們繼續看下去~(倒)

我又來啦~~ ( ˊㄧˋ)/

在mongo裡面相關權限及重要設定都在admin這個db中,讓我們做以下修改:
 > use admin
switched to db admin
> db.addUser('root','1234');
{ "n" : 0, "connectionId" : 3, "err" : null, "ok" : 1 }
{
        "user" : "root",
        "readOnly" : false,
        "pwd" : "985ddff43226255e09d56b21ad602f69",
        "_id" : ObjectId("509e6d9b3877acdb8eee898c")
}

> db.auth('root','1qaz')
1
之後呢,再以--auth參數重新開啟MongoDB即可

2012年11月8日 星期四

[宅] 宅男臥軌日記(9) - Filters in controller

好像在前面的筆記裡有提到before_filter,但是沒有特別深入研究,拜讀了ihower大大的部落格並上網查了一下相關文章以後,在這裡做一個筆記整理。


filters

在Rails中,我們可以定義method在進入action之前、之中、之後進行,分別使用before_filter、after_filter和around_filter來實做。其中最常見的應該就是用before_filter來做權限驗證了:
before_filter :authenticate
上面的例子中接在before_filter後的是一個symbol,代表著要呼叫哪個method
before_filter最主要的功能還是在跨action的共用參數傳遞,在前面關於layout & style的文章中就有呼叫method,然後method回傳一個instance varible的例子。

在ihower大大的部落格中有提到,除了method以外,還能傳入code block以及物件。若傳入的是物件,Rails就會執行物件中的filter方法。

那code block呢?


在filter中傳入參數

前面有提到要在filter傳入method,要用symbol來指定method的名稱,但是我們沒辦法在symbol中加入參數("before_filter authenticate(arg)"明顯是個錯誤的寫法Orz),所以要傳入參數就要用以下兩個方法了:

1. code block

before_filter do |c|
c.send(:authorize, args)
end

before_filter :only => :show do |controller|
  controller.instance_eval do
    authorize(args)
  end
end

以上兩個方法我也還沒驗證過,改天來試試看XD


2. inline procs

這種方法最直覺,不過我一樣還沒驗證過能不能用XD
before_filter {authorize(args)}





[觀察] "我可是優秀的大學生耶,怎麼可以漲我學費呢!?"




http://ppt.cc/gtNF
看了PTT上的這篇文章,我有很多話想說。
文中的大意是一個年輕人家境不好,老爸當別人的保人欠了600多萬,但是他也是一路半工半讀到了大二,重點是:"沒有學貸"

高等教育=社會福利?

首先,這是一個本來就應該被矯正的觀念,誰說年輕人一定要讀大學?如果說讀完大學是能及時投入戰力的,那我舉雙手贊成,多繳一點稅給你們讀都無妨。但是怎麼常常聽到大學生說"以後要幹嘛?不知道耶"或是"同學都讀研究所,那我就跟著讀啦"
當了太久的既得利益者,到大家發現資源分配不均以後,試圖矯正這個情況,大家就開始哇哇叫了....

讀大學好辛苦喔~每天讀書都讀不完了哭哭~ ^.<

摸著良心說,哪個學生沒有翹課過?更別提打混了。
如果你是真的很有心,認真的吸收每一分教育資源,即使在家境不好的狀況下也能自己打工堅持下去,那我覺得這樣的人到哪裡都能成功。
但是偏偏就有人是這樣:
(截圖自:http://goo.gl/jKHNL)

一個從高中開始貸款到大學畢業背了60萬的人,平均每年將近10萬。
那你等於是沒有試著還過嘛~每個月去超商打個工存個1萬元,扣掉生活費或多或少多少也能負擔一點了。
說到這裡,這個人究竟是真的認真讀書到沒有時間打工,還是覺得"反正學貸不用利息,不借白不借,晚點還沒差"?
我也剛脫離學生身分不久,知道當學生有多幸福,也知道大部分的學生過的是怎樣的日子。有些同學家境比較好,不用打工,零用錢可以爽爽花,但不代表家境不好的學生就得依樣畫葫蘆,跟風買什麼智慧型手機。要比手機名牌,為什麼不順便比一下人家的有錢老爸?有那個屁股才能拉那個屎,沒那個屁股請你乖乖去打工。

媒體已經很白癡了,拜託不要一起起舞

我這樣說不是要一竿子打翻一艘船,其實這樣的米蟲學生只是佔很少數。我雖然在學生階段也算是個米蟲,但好歹我也有在打工,也知道我享用了很多資源,不應該得了便宜還賣乖。但是越是米蟲的米蟲越會在媒體上大吐苦水,炒作群眾的情緒。
從前的大學生,是扮演著社會的先知先覺者,是推動革命、是推動社會運動的種子、是社會正義的捍衛者。當現在的大學生,享用著少少付出換回的高等教育資源時,還像個小孩子一樣的抱怨,我只覺得"又有一份教育資源被浪費了"


"什麼東西?
他們可是大~~~學生喔!"



2012年11月7日 星期三

[宅] javascript的模組化設計

最近看到幾篇相當棒的javascript模組化教學文章,在這邊收錄一下:
Javascript模块化编程(一):模块的写法
Javascript模块化编程(二):AMD规范
如何设计大规模 JavaScript 应用 (Part 1 - 概览)
Immediately-Invoked Function Expression (IIFE)

來為所學不精的自己做個學習筆記.....Q__Q


Immediately-Invoked Function Expression(IIFE)

IIFE其實就是像這樣的寫法:
var mod = (function(){var d=0; alert(d);})(); //typeof(mod)為"undifined"
也可以是
var mod = (function(){var d=0; alert(d);}()); //typeof(mod)為"undifined"
上面的寫法跟下面是不一樣的
var mod = (function(){var d=0; alert(d);}); //typeof(mod)為"function"

上面的寫法會直接執行function,但也就只是這樣而已,什麼都沒留下。若只是單純的IIFE其實只要這樣寫就行了:
(function(){var d=0; alert(d);})();



javascript的模組化

但既然要取得一個module我們當然要把內容給keep住,所以得要把需要的attribute包成object後return回來,所以改寫成這樣:
var mod = (function(){
    var d=0;
    var foo=function(){alert(d);};
    return {foo: foo}
})();
這樣我們就可以用mod.foo()來呼叫這個函數,也會alert 0,代表mod.foo()是找得到d的。
但是為什麼不直接向下面這樣定義object就好,還要這麼大費周章呢?
mod={foo: function(){alert(d);}, d:0}
主因就是用這樣的寫的話外部就能看到、也能改動mod.d,而模組化的mod中只找的到mod.foo,從外部是看不到d的。模組化後的物件可以含有像是java的private屬性~所以要讀寫這些屬性就得再寫getter和setter啦。



模組化的規範(CommonJS)

引述 Cat Chen大大的說法:一個模組系統至少要能解決兩個問題:dependencies的載入,以及private、public導出的分別。而CommonJS就是針對server side的javascript的相關規範,其中除了module的規範外還訂定了如Binary strings and buffers、Charset encodings、System process arguments、File system interface、Socket streams、Web server gateway interface、
Local and remote packages and package management等相關規範。更多SPEC可看官方文件:http://commonjs.org/impl/
其實Node.js就是遵照CommonJS的規範,從以下的官方範例就可以看出node.js的影子:

math.js
exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
        sum += args[i++];
    }
    return sum;
};

increment.js
var add = require('math').add;
exports.increment = function(val) {
    return add(val, 1);
};

program.js
var inc = require('increment').increment;
var a = 1;
inc(a); // 2
module.id == "program";

很明顯的,在CommonJS訂定的module架構中,require到的是module裡所定義的export。



瀏覽器端的模組化規範 - AMD

但是在前面提到的CommonJS規範並不適用於瀏覽器環境,主因是在本機端是從磁碟機載入相關lib,但在client端可是得一個個下載,在同步的情況下必須等到載入完後才會進行接下來的程式,所以頁面看起來就會像是短暫的當機一樣。
這也是為什麼在client端必須以非同步的方式載入(就是同時載入相關module)。但是以非同步的方式載入又會出現一個問題,如果前面的module還沒載入完畢,就被後面的程式所呼叫了呢?
"Asynchronous Module Definition"(AMD)就是為了避免這些問題的出現所訂定的規範。
不同於CommonJS的require,AMD中的require()需要兩個參數:
require([module], callback);
而目前最常見的AMD實作就是require.js了,可以去官網研究一下。



2012年11月6日 星期二

[宅] 宅男臥軌日記(8) - 關於layout & style



依不同controller來assign Layout

要依不同controller來指定對應Layout(template),可以用layout選項來指定。就拿開發中所遇到的問題來舉例:
在還沒登入或登入錯誤時,我想指定一個特別的layout來呈現,因為會這些頁面都會經過devise的controller,基於這樣的前提我們可以在application controller下做以下的調整:
class ApplicationController < ActionController::Base
  protect_from_forgery
  
  layout :layout_by_resource

  protected

  def layout_by_resource
    if devise_controller?
      "guest"
    else
      "application"
    end
  end
end

"devise_controller?"是devise所提供的helper,用以判斷所在的controller是否為devise。
其實在一般情況下我們是不用直接修改application controller的,直接修改要呈現不一樣Layout的controller就可以。但是因為我們看不到devise controller,無法對他做修改,所以就寫在他的父類別讓他繼承這個特性。
而指定layout時其實還可以再做一些微調,例如:
layout "special", :only => :index

layout "special", :except => [:show, :edit, :new]

大家應該可以注意到以上的兩行和一開始的範例寫法有點不一樣,一個是使用symbol、一個是使用字串,這在意義上是不一樣的。使用symbol是call function,而使用字串就是直接去找這個名字的layout了。

Style

在一開始的application.css.erb中,我們可以在comments中找到以下兩行設定
*= require_self
*= require_tree .
雖然這兩行是寫在註解中,但他們還是有功用的(要把他disable掉只要刪掉"="就好了)。
require_self代表最後匯整的內容要包含"這個檔案"
require_tree代表最後匯整的內容要包含"所在資料夾下所有的檔案"

我們也可以很單純的require單一css,ex:
require bootstrap


同一個controller下為不同的view分配style sheet

不同controller對應各別layout在前面已經提過,但是有時候我們想用一樣的layout,但是是不同的style該怎麼辦呢?這時我們可以在template中動態的載入style sheet:

在View中:
<%=stylesheet_link_tag @foo %>

在Controller中:
before_filter :get_css_file
def get_css_file
  @foo = defined?(@style_type) ? 'normal' : 'special'
end

或是在你自己的邏輯定義@foo。

blogger的RSS feed資料筆數限制

利用blogger來提交site map的部落客可能會發現預設的RSS feed最多只會抓25~26個左右的url:
http://blogname.blogspot.com/feeds/posts/default?alt=rss
這是因為大部分的服務都不接受超過512kb的feed,blogger就為了不要讓他爆掉所以只抓最多26個左右的文章。

為了能更有效的提交sitemap,我們就要改成以下的寫法:
http://blogname.blogspot.com/feeds/posts/default?alt=rss&max-results=100
只是要小心不要超過512kb就是了。

2012年11月5日 星期一

[宅] facebook開發筆記 - javascript SDK(下)

除了前面兩篇()所提到的FB的三個core method,另外也有幾個不介紹不行的好用method:

FB.Event.subscribe & FB.Event.unsubscribe

用來listen相關facebook事件,當特定事件出現時就執行callback function
FB.Event.subscribe(event, callback function)
讓我們來看一段程式碼:
FB.Event.subscribe('auth.sessionChange', function(response) {
     if (response.session) {
        alert(response.authResponse.accessToken);
        //使用者登入,資料寫入cookies裡
     } else {
        alert("Ohoh");
        //使用者登出,cookies被清掉
     }
});

除了範例中所提到的,我們還能訂閱以下的事件:

  • auth.login - fired when the auth status changes to connected
  • auth.logout - fired when your app notices that there is no longer a valid user
  • auth.authResponseChange - fired when the authResponse changes
  • auth.statusChange - fired when the status changes (see FB.getLoginStatus for additional information on what this means)


而傳給callback function的參數"response"長得像這樣:
{
  status: "",         /* Current status of the session */
  authResponse: {          /* Information about the current session */
    userID: ""          /* String representing the current user's ID */
    signedRequest: "",  /* String with the current signedRequest */
    expiresIn: "",      /* UNIX time when the session expires */
    accessToken: "",    /* Access token of the user */
  }
}

而至於unsubscribe的部分則是subscribe的相反,就是取消listen特定事件,可以自己試試看。


FB.login

顧名思義,就是以facebook登入所在網站。前提是你必須先用FB.init設定了SDK環境、設定appId。
FB.login(callback function, permissions scope)
來,讓我們再欣賞一段程式碼範例:
FB.login(function(response) {
   if (response.authResponse) {
     console.log('Welcome!  Fetching your information.... ');
     FB.api('/me', function(response) {
       console.log('Good to see you, ' + response.name + '.');
     });
   } else {
     console.log('User cancelled login or did not fully authorize.');
   }
}, {scope: 'user_about_me,email,user_likes'});
這邊要注意的是第二個參數是一個object,只有一組key:value,key一定是"scope",value是一連串以permission組成的String,中間以逗號分開。
scope中所指定的就是要從使用者那拿到的授權,要看詳細清單可以看facebook的官方文件


FB.getLoginStatus

基本上在一個facebook應用中我們需要判斷使用者是以下三個狀態中的哪一種:
  • 使用者登入了facebook也授權給你的應用程式 (connected)
  • 使用者登入了facebook但還沒授權給你的應用程式 (not_authorized)
  • 根本沒登入 (unknown)

這時我們就可以使用FB.getLoginStatus來搞清楚使用者的狀態
FB.getLoginStatus(callback function, force)
force是一個boolean,會強制reload使用者的登入狀態(預設為false)
我們再來偷一段程式碼來搞清楚他的用法:
FB.getLoginStatus(function(response) {
  if (response.status === 'connected') {
    // the user is logged in and has authenticated your
    // app, and response.authResponse supplies
    // the user's ID, a valid access token, a signed
    // request, and the time the access token 
    // and signed request each expire
    var uid = response.authResponse.userID;
    var accessToken = response.authResponse.accessToken;
  } else if (response.status === 'not_authorized') {
    // the user is logged in to Facebook, 
    // but has not authenticated your app
  } else {
    // the user isn't logged in to Facebook.
  }
 });

response object:
{
    status: 'connected',
    authResponse: {
        accessToken: '...',
        expiresIn:'...',
        signedRequest:'...',
        userID:'...'
    }
}


總結

基本上用facebook javascript SDK開發中常用的、必用的大致上都在這三篇提過了。但是真的要用得很熟練還是要多翻facebook的官方文件。希望這三篇教學筆記能幫助路過的facebook入門開發者。

[宅] facebook開發筆記 - javascript SDK(中)


FB(object)

FB是javascript SDK主要提供服務的物件,在載入函式庫時就會被建立在window底下(FB等於window.FB)。
FB提供很多功能,其中最核心的function有三種:
  • FB.api
  • FB.init
  • FB.ui

前面其實已經使用過FB.init()這個funtion,我們知道它主要是用來初始化SDK環境。為了更了解FB這個object還有些什麼attribute和function,我把它給parse一遍,印出來讓大家能一目了然:
_callbacks: object
api: function
getLoginStatus: function
getAuthResponse: function
getAccessToken: function
getUserID: function
login: function
logout: function
Auth: object
-->getLoginStatus: function
-->fetchLoginStatus: function
-->setAuthResponse: function
-->getAuthResponse: function
-->parseSignedRequest: function
-->xdResponseWrapper: function
Canvas: object
-->isTabIframe: function
-->setSize: function
-->setAutoGrow: function
-->getPageInfo: function
-->scrollTo: function
-->setDoneLoading: function
-->startTimer: function
-->stopTimer: function
-->hideFlashElement: function
-->showFlashElement: function
-->getHash: function
-->setHash: function
-->setUrlHandler: function
-->Prefetcher: object
CanvasInsights: object
-->setDoneLoading: function
share: function
publish: function
addFriend: function
Data: object
-->query: function
-->waitOn: function
-->process: function
Event: object
-->subscribers: function
-->subscribe: function
-->unsubscribe: function
-->monitor: function
-->clear: function
-->fire: function
EventProvider: object
-->subscribers: function
-->subscribe: function
-->unsubscribe: function
-->monitor: function
-->clear: function
-->fire: function
Frictionless: object
-->init: function
-->isAllowed: function
init: function
JSON: object
-->stringify: function
-->parse: function
UA: object
-->nativeApp: function
ui: function
XFBML: object
-->parse: function
-->RecommendationsBar: object

接著我們要介紹幾個重要的function


FB.api

在上一篇提到的API,除了以網址的方式來query資料,我們也能用FB.api()來執行facebook API。(主要是FQL及graph API,login的部分有FB.login()來處理,而REST API已經被捨棄了)
FB.api最多需要四個參數,分別是url、method、提供給API的參數(object)、callback函式:

姓名類型簡介
pathStringthe url path
methodStringthe http method (default "GET")
paramsObjectthe parameters for the query
cbFunctionthe callback function to handle the response



這邊就以FQL來舉個例子,先來看FQL中friends表單的欄位:

可索引姓名類型簡介
*uid1intThe user ID of the first user in a particular friendship link.
*uid2intThe user ID of the second user in a particular friendship link.


今天我們想從FQL中的friends表單中找出使用者的好友,寫成FQL語句就會是這樣:
SELECT uid2 FROM friend WHERE uid1=me()

如果要使用FB.api的話就要寫成這樣:
FB.api(
    "/fql", //path
    {q:"SELECT uid2 FROM friend WHERE uid1=me()"}, //parameter
    function(d) {window.d = d;} //callback function
 );

你可能會發現上面是沒有傳入method的,那是因為如果沒有傳值進去method會預設為GET,所以還是可以正常運作。所以在GET的方法下我們當然也可以寫成:

FB.api(
    "/fql?q=SELECT+uid2+FROM+friend+WHERE+uid1=me()", //path
    function(d) {window.d = d;} //callback function
 );


到了這邊大家可能會發現我一直都沒使用到access_token,這可不是筆誤。使用SDK時,它會自動幫你傳acess_token(我猜的),所以不用特別把它傳過去。但如果你是使用http來query的話就一定要了,所以上面的功能就要寫成這樣:
https://graph.facebook.com/fql?q=SELECT uid2 FROM friend WHERE uid1=me()&access_token=...


FB.ui

主要是用來產生dialog與使用者做互動,例如"發佈到塗鴉牆"、"邀請好友"、"分享連結"、"取得應用程式授權"等等都可以用FB.ui來與使用者溝通,也是個比較禮貌的做法。
那什麼是不禮貌的做法勒?例如說在取得一次使用者的發布塗鴉牆授權後,使用FB.api來直接發布:
FB.api('/me/feed', 'post', { message: body }, function(response){});
也因為FB.ui在動作時都會以彈出視窗讓你知道,所以照理來說應該是不用事先取得這些動作的授權,讓我們來看看要怎樣發布資訊到使用者牆上:
FB.ui(
  {
    method: 'feed',
    name: 'Facebook Dialogs',
    link: 'http://developers.facebook.com/docs/reference/dialogs/',
    picture: 'http://fbrell.com/f8.jpg',
    caption: 'Reference Documentation',
    description: 'Dialogs provide a simple, consistent interface for applications to interface with users.'
  },
  function(response) {
    if (response && response.post_id) {
      alert('Post was published.');
    } else {
      alert('Post was not published.');
    }
  }
);

[宅] facebook開發筆記 - javascript SDK(上)

前面兩篇講到了facebook API,現在我們來講講SDK。

API(Application Programming Interface)直譯成中文是"應用程式介面":只要藉由對方提供的介面丟東西進去就會回傳結果給你。
SDK(Software Development Kit)比較不一樣的是他需要在本機安裝他所提供的函式庫實際跑在本機端,而不是單純使用遠端的服務。

facebook javascript SDK

facebook目前提供JavaScript SDK、PHP SDK、iOS SDK、Android SDK四種開發環境,因為我目前只做web開發,而且也不只用php,所以選擇javascript SDK來做研究、開發。

安裝函式庫&初始化

要使用javascript SDK,必須先include進函式庫,我們來看看以下範例:
<div id="fb-root"></div>
<script>
  window.fbAsyncInit = function() {
    // init the FB JS SDK
    FB.init({
      appId      : 'YOUR_APP_ID', // App ID from the App Dashboard
      channelUrl : '//WWW.YOUR_DOMAIN.COM/channel.html', // Channel File for x-domain communication
      status     : true, // check the login status upon init?
      cookie     : true, // set sessions cookies to allow your server to access the session?
      xfbml      : true  // parse XFBML tags on this page?
    });

    // Additional initialization code such as adding Event Listeners goes here

  };

  // Load the SDK's source Asynchronously
  (function(d, debug){
     var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
     if (d.getElementById(id)) {return;}
     js = d.createElement('script'); js.id = id; js.async = true;
     js.src = "//connect.facebook.net/en_US/all" + (debug ? "/debug" : "") + ".js";
     ref.parentNode.insertBefore(js, ref);
   }(document, /*debug*/ false));
</script>

第一部份(藍字) - "fb-root tag"

只要有facebook javascript SDK的地方就要有fb-root。
但是你會發現即使不加它,程式好像也能正常執行,這是因為在初始化時SDK找不到fb-root,就會幫你新創一個,並在console log中顯示"The "fb-root" div has not been created, auto-creating."
在facebook的官方文件中有提到,若將fb-root設為display: none 或 visibility: hidden,在萬惡IE中可能會不正常運作,但是基本上你也不用考慮它會影響排版,因為初始化時就會把它設為position:absolute且top:-1000之類的,絕對不會讓你看到。所以這部份很單純的嵌入這個小小的div就好。


第二部份(綠字) - 初始化設定

這部份很關鍵,第一部份不用改、第三部份也可以不用改,但是這個地方你至少必須填入你的appId,不然是不能work的,其他參數可以不用改沒關係。
雖然說除了appId以外的參數保留預設值就可以了,但是channelUrl似乎滿值得一提的,所以我在網路上查了一下,在stackoverflow上找到了一個不錯的解釋:

The channel file is to provide a way to do cross domain communication between FB's servers and your own. The reason for this is their tight control over access tokens. You must authenticate a redirect url and app id to retrieve this access token. In order for them to pass you the token, they hit your URL with the access token in the hash. With the Channel URL, they get to pass this token to themselves (their JavaScript running on your domain).
This channel file can then communicate the access token to your active page.

facebook提供給開發者的大部分服務幾乎都得靠access_token來驗證權限,而要取得access_token必須要經過facebook的兩個檢查:redirect urlapp id,他們必須和你在facebook developer那邊所註冊的資料是吻合的(redirect url得在登記的domain下)。也因為這個原因,在沒有預設chanel的狀況下,SDK會強制在你的頁面塞入一個隱藏的iframe以便在你的頁面載入social plugins,這樣一來會造成載入時間長以及效率低落的問題。所以為了提高效能,facebook建議開發者建立http://www.yourdomain.com/channel.html這個檔案,檔案中只要有一行程式碼即可:
<script src="//connect.facebook.net/en_US/all.js"></script>


第三部份(紅字) - 載入facebook javascript SDK

其實只要照範例把這段貼上去即可,不太需要變動它。
這段code主要是檢查document中是否已載入了SDK,如果沒有就嵌入SDK的js檔。在這段code裡面你會看到它所載入的js檔是"//connect.facebook.net/en_US/all.js",其中的en_US是代表洋人美語語系,若要改成台灣用的繁中語系,只要把en_US換成zh_TW就好了。


2012年11月2日 星期五

[宅] facebook開發日誌 - facebook API入門(2)

接續前一篇的acess token講解,這篇會介紹一下facebook所提供的4種API
說是這麼說,但其實這篇會比較短,因為真的要詳盡介紹每個API的話乾脆直接買本書看比較快。



"那你他O幹嘛不兩篇寫一起就好?"



因為我要寫兩篇,
感覺比較有成就感,
順便騙騙文章數。

而且我不只要騙文章數,還要騙字數


騙字數的前情提要


<!-- 複製貼上 開始 -->
facebook提供開發者四種API:

  • Login
  • Graph API
  • FQL
  • Legacy REST

<!-- 複製貼上 結束 -->



Login

顧名思義就是提供登入的方法,可能是網站也可能是APP,這邊就不詳述。


Graph API

主要是用於社群用途,只要有適當的存取代碼(access token),就可以網址加上參數的方式去query需要的data,回傳的形式可能是json或單純的網址。這裡有個很簡單的範例,只要在空格內輸入你的UID就行了(不知道自己的uid請回去上一篇看看怎麼取得自己的基本資料,資料中的"id"就是了)

http://graph.facebook.com//picture GO!


簡單的一個網址就可以取回使用者的頭像了。

"但是剛剛不是才說要取得access token嗎?怎麼這麼輕易就取得了使用者的頭像了勒?"


這是因為facebook對資料有分級,以頭像來說好了,無論你和他是不是朋友,大部分facebook使用者的頭像都是可見的,存取這類開放的資料當然不需要特別的授權。甚至很多人的興趣、學歷、感情狀態等資料都是沒有設隱私的,我們就可以在沒有access token的情況下query到這些資料,就跟上一篇講到的一樣。




FQL

FQL是在取得適當access token的情況下,以類似SQL語句的方式來query資料。能做的事情更多,但對沒碰過SQL的初學者來說就比較沒那麼容易上手,只要稍微碰過SQL的人就會發現FQL相當好用也很強大
為了達到一開始所提到的收集照片的功能,我寫了以下的一段code:
SELECT pid, src, src_big, caption FROM photo
WHERE aid IN (
  SELECT aid, owner, name, object_id 
  FROM album WHERE owner=#{me}
)
你可以把這段code貼在Graph API Explorer上,但是首先記得要切換成FQL模式,並再點一次"取得存取代碼"來增加"user_photos"這個存取權限。點選送出後,看看會發生什麼事。
(記得把#{ME}換成你的ID啊!)

Legacy REST

facebook已經宣佈不建議使用這種API,並建議使用graph api,所以當然果斷捨棄不解釋
"We are in the process of deprecating the REST API."


[宅] facebook開發日誌 - facebook API入門(1)

在開發facebook相關應用的時候一直沒有好好地做一個整理,
最近有個應用需要query使用者在timeline上發的照片,正好趁此筆記一下FQL這個東東結果一不小心就寫成API的介紹文。

前言

facebook提供開發者四種API:

  • Login
  • Graph API
  • FQL
  • Legacy REST

但在開始介紹API之前必須先提授權碼(access token)的概念,讓我們使用Graph API Explorer這個好用的facebook開發工具來一步一步了解access token的概念。
首先,登入 http://developers.facebook.com/tools/explorer/ ,你會看到以下畫面:


先在"GET"旁邊的輸入欄打"ME",然後送出。如果你從來沒有使用過這個工具的話,就會看到以下訊息:
{
  "error": {
    "message": "An active access token must be used to query information about the current user.", 
    "type": "OAuthException", 
    "code": 2500
  }
}
這代表你沒有權限存取現在的facebook使用者(你自己)的資料,但在取得這個權限之前,我們先來做另一個小試驗:在同一個地方打"zuck",相信你會看到以下訊息:
{
  "id": "4", 
  "name": "Mark Zuckerberg", 
  "first_name": "Mark", 
  "last_name": "Zuckerberg", 
  "link": "http://www.facebook.com/zuck", 
  "username": "zuck", 
  "gender": "male", 
  "locale": "en_US"
}
眼尖的你可能馬上就發現了這是facebook創辦人Mark Zuckberg的基本資料,酷吧!
但是為什麼自己的資料拿不到,卻可以拿到別人的資料勒?因為Zuckberg是特例嗎?一定是這樣子的吧哈哈哈!




其實不是特例,因為你打別人的名字也是一樣,Peter、John、Mary之類的菜市場名都可以試試,甚至你如果曾在facebook上取過英文也可以試試看....

"X!真的可以耶!這是怎麼回事!?"

這是因為"ME"是取得任何正在瀏覽這個網頁的facebook user的參數,這樣開發者就可以很靈活的寫相關應用。所以雖然基本上不用任何acess token也可以取得特定使用者的基本資料(使用特定ID),但要用ME來靈活運用使用者資料就得要取得你的授權(acess token)了。

所以前面出現的"OAuthException"就是因為Graph API Explorer還沒取得你的授權,所以不能用ME來取得你的資料的錯誤提示。現在我們就點選Graph API Explorer頁面上的"取得存取代碼"這個按鈕來讓它取得你的授權,然後就會彈出這個視窗:


上面佈滿了各種選項,代表了你要讓這個應用程式存取那些權限,點選"user_about_me"以後送出就會看到熟悉的畫面:


現在你應該知道facebook app的登入畫面是怎麼來的了

之後Graph API Explorer就可以使用你的基本資料,輸入"ME"之後也會順利的出現相關訊息,這個簡單的步驟解釋了app從登入到使用你的資料的基本流程,也是facebook應用開發者的第一步。

而Graph API Explorer可不只是facebook開發的入門工具,畢竟facebook所提供的API還不少,所以即使是有開發經驗的工程師也會時常使用這個工具來驗證自己的邏輯是否可以正確運作。

身為一個開發者,只知道這些是不夠的,請讓我們往下一篇邁進!

[宅] 宅男臥軌日記(7) - .nil? , .empty?, .blank? 的區別


.nil? 和.empty? 是ruby的方法,而.blank? 是rails的方法
這三個method都是判斷是否為空值。


.nil?

判斷對象是否存在(nil)。不存在的對像都是nil的

.empty?

對象已經存在,判斷是否為空字串。
比如一個字符串是否為空串,或者一個數組中是否有值。

.blank?

具有以上兩個method的特性,railsAPI:
An object is blank if it’s false, empty, or a whitespace string. For example, “”, “ ”, nil, [], and {} are all blank.