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

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.

2012年10月30日 星期二

[宅] javascript筆記(8) - 全域變數&匿名函數

最近在PTT ajax版看到一篇非常不錯的筆記,正好把之前雜亂無章的理解給重新整理了一遍。
原文:http://disp.cc/b/11-4CrK (Knuckles@PTT)

使用全域變數時,由於要往上查找全域,造成效能低落,所以應盡量避免大量使用。
文中作者提到匿名變數的方法:
(function(x){ /*...*/ })(x);
自己寫lib時也曾用這個方法來寫。雖然在匿名函式外看不到裡面所定義的function和變數,但是可以在匿名函式內綁定function和外部觸發事件。也可以將匿名函式裡特定物件設為全域變數,由於scope的關係,外面看的到這個object,這個object也看得到匿名函式裡的其他function和變數。我們以ember.js開頭的一小段程式碼來做了解:
(function() {
  ...
  /**
  @class Ember
  */
  if ('undefined' === typeof Ember) {
    Ember = {};

    if ('undefined' !== typeof window) {
      window.Em = window.Ember = Em = Ember;
    }
  }
  ...
})();
當一個全域變數被定義時,等於是加入到window這個object底下。所以Ember={}跟window.Ember={}是一樣的。
而Ember這個object可能有很多function,例如Ember.deprecate,就可以在全域被看到。但如果Ember.deprecate需要使用一些varible或function,這些東西都必須放在Ember所被定義的匿名函式裡。

從Ember的寫法也能看出來,當include一個lib進來的時候,通常會定義一個特定名稱的Object再將相關的變數及函式都塞在這個Object底下,避免一不小心就重複命名了。

2012年10月29日 星期一

[宅] 宅男臥軌日記(6) - %Q, %q, %W, %w, %x, %r, %s

%Q( )

能將括弧中的內容轉成雙引號框成的字串,
而%Q後面未必要用括弧,也可以是!、[ ]、+等符號,例如:
%Q! !、%Q[ ]、%Q+ +

%q( )

功能跟大寫Q差不多,只是將內容換成單引號框成的字串。

※Notice: 在Ruby中定義字串時最好使用雙引號,因為只有在雙引號中才能使用#{var}來嵌入程式碼

%W( )

將括弧中的內容轉換成雙引號的string array,看下面的程式碼會更容易理解
>> %W(#{foo} Bar Bar\ with\ space)
=> ["Foo", "Bar", "Bar with space"]

%w( )

和大寫W是差不多的,只是將雙引號變單引號


%r( )

正規表示式,如下:
>> %r(/home/#{foo})
=> "/\\/home\\/Foo/"

%s( )

用於符號(symbol)
>> %s(foo)
=> :foo
>> %s(foo bar)
=> :"foo bar"
>> %s(#{foo} bar)
=> :"\#{foo} bar"

[宅] 宅男臥軌日記(5) - 資料表關聯方法: includes & joins

用一行程式碼大家應該就能明白:
user = User.includes(:authorizations).where("authorizations.provider" => 'facebook' , "authorizations.uid" => uid).first

includes跟joins功能挺像,引用ihower大大的部落格內的說法:
透過joins抓出來的event物件是沒有包括其關連物件的。如果需要其關連物件的資料,會使用includes。(出處:http://ihower.tw/rails3/activerecord-relationships.html)
但有一個差別在於includes所找到的model內的readonly屬性會是false,而joins所回傳的將是true。這造成了很大的影響:"用joins所找到的model不能被修改",所以當試圖修改model的值時,會回傳ActiveRecord::ReadOnlyRecord error.

2012年10月28日 星期日

[宅] 宅男臥軌日記(4) - class及instance的method定義

在定義class中的method時,要注意究竟是給instance使用還是class

當以如下方式定義method時,代表這個method是要給class的instance使用
class User
  def say_hello
    puts "Hello"
  end
end
所以當執行User.say_hello時就會發生找不到method的情況,但在以下情形會是正常的:
user = User.new
user.say_hello
=>"Hello"

所以如果一個method希望能被class所使用,必須以以下方式定義:
class User
  def User.say_hello
    puts "Hello"
  end
end
或是
class User
  def self.say_hello
    puts "Hello"
  end
end
當然也可以用extend從module中mixin進來

[宅] 宅男臥軌日記(3) - load或require路徑

在ROR中,若想load或require其他module進來,要將module放在哪哩?
我們可以看看config/application.rb中的設定:
# Custom directories with classes and modules you want to be autoloadable.
    config.autoload_paths += %W(#{config.root}/lib)
上面這一段就是指定module所在的資料夾,其中config.root是指config/的母目錄。所以這樣的設定之下,我們就要將module放在lib/底下,在class中引用module時,ROR就會知道要到lib/底下找。

要注意的是檔名如果有多字都是小寫,字和字之間用底線區隔,像這樣:
omniauth_callbacks.rb

裡面的module得要這樣命名:
module OmniauthCallbacks

2012年10月27日 星期六

[宅] 宅男臥軌日記(2) - ruby中的include, extend及require

include vs. extend

在ruby的世界中,class只能單一繼承,而為了保有彈性,ruby提供了module的概念。
module是一個method的集合,可讓class去混入(mixin)一個module來取得這些method,而mixin的過程可用include或extend來達到。
但是include和extend的差別在哪?我們可以從以下的程式碼得知:

module Foo
  def foo
    puts 'heyyyyoooo!'
  end
end


class Bar
  include Foo
end

Bar.new.foo # heyyyyoooo!
Bar.foo # NoMethodError: undefined method ‘foo’ for Bar:Class


class Baz
  extend Foo
end

Baz.foo # heyyyyoooo!
Baz.new.foo # NoMethodError: undefined method ‘foo’ for #<Baz:0x1e708>

簡而言之,include是讓這個class所產生的instance繼承module中的method,有點javascript中function.prototype的味道。
extend則是讓這個class具有module中的method,卻不會繼承給instance。


require

在講require之前必須先提到load,load的作用在於將不同檔案的module給mix進來,概念上類似於C的include。而require的作用就類似於load,只是在require只會mix一次,當發現之前已經mix過同一個檔案時,require就會回傳false。
require相對load會少掉不必要的mix動作,但在module時常被更新的狀態下,最好使用load來確保自己抓到的檔案是最新的。

2012年10月26日 星期五

[宅] 菜鳥出槌筆記 - 本地化設置錯誤

今天要執行mongo指令時,一直出現以下錯誤訊息:

terminate called after throwing an instance of 'std::runtime_error'

  what():  locale::facet::_S_create_c_locale name not valid

Aborted (core dumped)

上網查了一下發現是本地設定的問題,所以在mongo指令之前加上"LC_ALL=C"就可以成功執行了
LC_ALL=C mongo

[宅] node.js開發筆記(2) - 以express-vhost來建立虛擬主機 & 背景執行node.js

為了在同一個主機上搭載不同網域的網站,在npm上survey了一下相關套件,最後找到了express-vhost
npm install express-vhost
express-vhost是express的middle-ware,可以透過resiter將特定的網域綁定指定function,來達到以網域做分流的功能。詳情請看:https://github.com/vast-eng/express-vhost

我們希望node能夠在背景執行,而且即使登出主機也能繼續提供服務。
這時我們就要使用forever,他能夠讓你的js檔在背景執行,以下是安裝及啟動方法:
npm install -g forever//記得要設定為全域
sudo forever start server.js//若要以80port開啟服務,記得要以root權限執行

比較要注意的是,利用forever來執行js檔是沒辦法讓你看到console.log的,所以在啟動之前最好用node指令執行一次,確定服務正常再使用forever來開啟sever。尤其是你的server預設listen 80port時,忘了加sudo又沒有相關提示可能會讓你白白debug老半天還找不出原因。

話說其實node最好不要listen 80port,而是用其他伺服器程式來轉接給他,詳細原因可能下一篇再解釋。

2012年10月25日 星期四

[宅] node.js開發筆記(1) - 從安裝到express

最近開始使用node來開發一個vhost 應用~
因為之前是玩的時候是使用windows的環境,這次改在Ubuntu上開發,重新安裝的過程中也順便以筆記整理一下。

<step1> 安裝相關需求
因為這個環境空空如也,非常的乾淨,所以事先必須把一些基本的需求給建立起來
sudo apt-get install python-software-properties
sudo apt-get install make
sudo apt-get install g++
sudo apt-get install git
以上都搞定後就可以從github上下載下來並安裝了:
git clone https://github.com/joyent/node.git
cd node
git checkout v0.6.7
./configure
make
sudo make install
這樣就裝好了~

<step2>安裝npm
npm之於node就好比gem之於ROR,
gem之於ROR就好比apt-get之於Ubuntu....科科

咳,總之npm是node的套件安裝工具,
比較常用到的指令不外乎
npm install -g {你要裝的套件} //-g是代表這個套件可被全域使用,可以不用加
npm search {你要查的關鍵字}
npm list //列出你已安裝的套件

在安裝套件後,npm會把套件的module放在當下資料夾的"node_modules"資料夾中,
在執行node時就可以require該module,前提是執行的js檔所在的資料夾下要有"node_modules"資料夾且裡面那些module才行。
不過照理來說,我安裝的全域套件不用特別塞在裡面就找得到了,事實證明是不行啊~Orz
鳩竟是怎麼回事還得再查查~

<step3>用npm安裝express
express必裝!!!怎麼裝請看下面:
sudo npm install -g express
當要全域安裝時記得加sudo,不然會失敗

<step4>啟動server
我們要使用express的API來設定特定routing的response,將以下程式碼寫入"server.js":
// create server.
    var express = require('express'),
        app = express(),
        port = 1337; 

    app.listen(port);

    // normal style
    app.get('/', function(req, res){
        res.send('hello world');
    });
執行:
node server.js

就可以在localhost:1337上看到"hello world",代表server正常的運行囉~


2012年10月10日 星期三

[宅] coffeescript第一課 - "原來不能這樣寫"

為了開發node以及導入同伴的python開發經驗,從今天開始接觸coffeesscript,來體驗他簡短程式碼並最佳化成javascript的能力。

一開始當然是要體驗一下他和javascript的不同啦,
首先是scope的習慣,在javascript中我們會習慣用大括號來圈起範圍,而在coffeescipt中我們使用縮排。
再來,在javascript中很常見的 for( i in obj)在coffeescript中是會有問題的,因為他會把這段程式轉化為:

for (_i = 0, _len = obj.length; _i < _len; _i++) {
}

而object預設根本沒有length,而這段程式碼很明顯就是為了array寫的,運用在array上當然也沒問題。那要loop物件中的attr該怎麼辦呢?我們只需要改成 for( i of obj)即可,而因為coffeescript具有List comprehension的特性,讓他能用以 下的方式來寫loop:

alert item for item of v

而不用

for( item of v){
     alert(v[item])
}

超快的,對吧?

2012年9月28日 星期五

[宅] html5筆記 - Local Storage


最近用Local Storage的html5當成一個類似client端的DB來做一點小應用
當中發現了一點小心得,在此紀錄一下:

localStorage是html5規格下,直接存在於window物件底下的Object,他的腳色跟cookie有點像,但是容量比較大,也不用每次都傳回給server。
美中不足的是,你要存入的資料都必須以string的形式做儲存
localStorage.test={};//這樣應該是Object吧
typeof(localStorage.test);//結果是String....Orz
所以在使用他的時候我們就要稍微用一點心思了,而JSON的.stringify()以及.parse()正好適合處理這樣的問題!

首先,我們在localStorage中建立一個名為test的property:
localStorage.test="{}";
為什麼要刻意在字串中加入上下括號呢?這是因為雖然我們只能以字串方式存入localStorage,但是可以寫成json的格式,再以JSON.parse()將他轉成真正的物件,讀寫完畢後再以JSON.stringify()轉成字串,存回去就好了。

[宅] javascript筆記(7) - javascriptMVC-Model

最近因為工作的關係開始接觸javascriptMVC,雖然網路上的評價不像Ember那麼的好,也沒有所謂的UI-binding,但是開發團隊很用心的寫了fixture、document等好用的功能,甚至支援command line產出程式碼,一開始用起來的感覺很像是client端的ROR~
至於這些功能有沒有在Ember上被實現我就不知道了,或許有機會可以試試看。

Model:

稍微接觸過MVC架構的朋友應該就知道,Model是負責跟資料庫溝通的部分。
在client端的MVC架構雖然無法直接存取資料庫,但在javascriptMVC中內建了restful的方法,可以經由save(對應update), find(對應select), findAll(select all)等method來呼叫定義的restful:
$.Model('Todo',{
    findAll: 'GET /todos.json',
    findOne: 'GET /todos/{id}.json',
    create:  'POST /todos.json',
    update:  'PUT /todos/{id}.json',
    destroy: 'DELETE /todos/{id}.json'
},{});
這樣就產生了一個Todo的class,而他有著這些restful路徑,我們可以利用Todo來新增一些instance如下:
var todo = new Todo();
這時你可能會想問,那我要怎麼寫入可以被所有instance共同繼承的property呢?
在一開始創建Todo這個class時,我們可以在第三個parameter(型別為Object)內寫入我們要預設的property,就是我標示為紅色的地方啦~

所以假設我在一開始建立Todo這個Model時希望他的instance都能有一個叫做"spotlight"的function以及priority的屬性,我們就要改寫成這樣:

$.Model('Todo',{
    findAll: 'GET /todos.json',
    findOne: 'GET /todos/{id}.json',
    create:  'POST /todos.json',
    update:  'PUT /todos/{id}.json',
    destroy: 'DELETE /todos/{id}.json'
},{
    spotlight : function(){},
    priority : 0
}
);
如此一來被建立出來的todo instance應該會有如下的properties:

priority(number) spotlight(function) constructor(function) Class(function) setup(function) update(function) errors(function) attr(function) bind(function) unbind(function) _updateProperty(function) removeAttr(function) attrs(function) serialize(function) isNew(function) save(function) destroy(function) identity(function) elements(function) hookup(function) created(function) updated(function) destroyed(function) proxy(function) callback(function)

其中save這個function就是對自己這個instance做update的工作,但這時候你可能會發現一但我真的執行了save這個method,傳出的data中卻空空如也,這是因為你不是透過建構子或是.attr( )來對這個instance建立property的!不然總不能要這個instance傻傻地將以上一長串properties全部update上去吧?
為了要讓這個instance知道哪些是自己應該要儲存的attributes,我們可以在建立instance時加上建構子的參數,如下:
var todo = new Todo( {'priority' : 0, 'spotlight' : function(){}} );
或是在創建之後用.attr( )來加入attributes:
todo.attr('priority',0);
這時你可能會想問:「那這個instance是怎麼記錄這些attribute是要被上傳的呢?」
你會發現在這個instance中有一個叫attrs的function,呼叫他之後會返回一個Object,裡面有你到目前為止所增加的attributes,也就是save時所上傳的data!

所以這代表每次每次我們要修改attributes都得透過attr嗎?
答案是不必的~透過建構子或是.attr( )來建立attriutes只是順便做一個類似"註冊"的動作,之後即使是直接進行如下的操作,呼叫attrs後也可以發現數值真的有被修改到:
todo.priorirt=1;
暫時先這樣。

2012年9月24日 星期一

[宅] javascript筆記(6) - jQuery plugin

在使用jqgrid時一定常看到這種用法:
$("#grid").jqgrid(.....);

這種對jQuery元素做操作的method就是利用extend把它綁在上面的,而extend又分兩種用法:
jQuery.extend
jQuery.fn.extend

第一種jQuery.extend可以看作是直接在jQuery上加入一個方法,舉個網路上找到的例子:

 <script type="text/javascript">
        jQuery.extend({
            add: function(a, b) {
                return a + b;
            }
        });
        alert($.add(3, 4)); //7
 </script>
這樣的功能可以直接用"$.定義的function"來執行。

第二種jQuery.fn.extend只能用在jQuery元素上,jqgrid用的就是這種方法了,當要對元素做操作時只要用this即可:

 <script type="text/javascript">
        jQuery.fn.extend({
            red: function() {
                return $(this).css('color', '#ff0000');
            }
        });
        $('#test').red();
</script>

在網路上查了一下也有發現不用extend來做plugin的:

$.fn.hilight = function() {
  // Our plugin implementation code goes here.
};
但這之間有什麼差異就還要再做研究了~

更詳細一點的介紹與範例可以看:ericsk.net-擴充 jQuery


2012年9月14日 星期五

[宅] ROR筆記(1) - "ActiveRecord::ConnectionNotEstablished"問題

因為考慮到後期的延伸以及功能性,我捨棄了預設與ROR綁定的sqlite,改用postgresql來做為網站的資料庫,在設定好database.yml後,在routing時卻發生了ActiveRecord::ConnectionNotEstablished的錯誤訊息。上網survey一下才發現原來是資料庫的相關設定不夠完全,所以整理了一下設定資料庫所必需的步驟:
(單純只是個page reguest也要過sql那一關...是要整誰...Orz)

1. database.yml : 

在這個檔案中你可能會看到三個設定分別是development, test, production,分別是在開發、測試、正式上線時所用的三種模式,基本上應該是要長這樣:

development:
  adapter: postgresql
  database:[給ROR使用的資料庫]
  pool: 5
  timeout: 5000
  username: [給ROR使用的帳號,必須有全域的CREAT權限]
  password: [密碼]
  host: [你的database路徑]
尤其是最後一句特別要注意,因為預設使用的sqlite是不用安裝且在本機端運行的database,所以在原本的設定並沒有host這個項目,要自己加上去。
以上是以development mode為例,其他的mode也是差不多的概念。

2. gemfile:

這個檔案可以在專案的根目錄裡找到,其中有一行會是 : gem 'sqlite3'
這是提示系統說我們需要用到sqlite的gem,在這邊請改成" gem 'pg' "

3. 安裝gem pg:

在gemfile中做了使用postgres的設定卻沒安裝postgres的gem是跑不起來的,這時請在commend line中打"gem install pg"就會安裝好了。
貓桑一開始安裝成gem install postgres也是成功的,但卻發現那是比較舊的postgres driver,在2008年後就不被支援了,當然也跑不起來。

2012年9月11日 星期二

[宅] javascript筆記(5) - 關於z-index、visibility和滑鼠點擊事件


好吧,我承認這篇的主題很亂...


z-index、visibility


首先,z-index的概念很簡單,只要他的值越大代表離你越近,也就是越上層。
visibility則是可以設定成"hidden"或"visible"。

這兩個css屬性是我今天在寫一個light-box的時候所用到的,程式碼如下:

<div id="sb-container" style="visibility: hidden;">
</div>
<div onclick="$('#sb-container').css('visibility','hidden')" style="background-color: black; height: 100%; left: 0px; opacity: 0.8; position: fixed; top: 0px; width: 100%; z-index: 9;">
</div>
<br />
<div id="info-box" style="background-color: white; height: 440px; position: fixed; top: 100px; width: 540px; z-index: 10;">


最外層的"sb-container"是把裡面的兩個div裝起來的這不用多說,裡面的第一個div經由以上的設定後會向一層半透明(opacity:0.8)的遮罩蓋住整個可視範圍。

而在剛創建這個物件時發現有些網頁物件的層級比他稍高,所以我增加了z-index的屬性並設定為比較高的數字(9),這層遮罩就蓋住了整個頁面。

在遮罩之上還要有一個類似dialog的視窗來顯示內容,而他勢必要比遮罩還上面一層,否則會被遮掉,這裡可以看到他的z-index為10。

我想讓這個功能跟facebook顯示圖片的方式一樣,點選黑黑的地方就可以把這個 light  box關掉,所以大家可以看到在遮罩的部分有個$("#sb-container").css("visibility","hidden")的onclick事件。

運用z-index和visibility就可以做出簡單的 light  box效果了。



要補充說明的是,在一般的佈版及配置下,即使visibility為hidden的物件還是會占空間的,若要在隱藏時不要占用空間,可用display:none;


滑鼠點擊事件


當滑鼠點下時,我們可從e中找到一個button屬性,內容可能是0~2其中一個,分別代表是哪個滑鼠按鍵被按下:
0 - 左鍵
1 - 中鍵
2 - 右鍵

2012年9月5日 星期三

[宅] javascript筆記(4) - jQuery selector


以對<table id="t">下所有<td>加掛click事件為例,多半會寫成: 
$("#t td").live("click", fn); 或 $("#t").delegate("td", "click", fn);

學習了學習了~感謝暗黑執行續大大
(http://blog.darkthread.net/post-2011-10-14-delegate-vs-live.aspx)

2012年8月30日 星期四

[宅] javascript筆記(3) - 正規表示式以及undefined的定義



About 正規表示式...

今天在coding的時候發現傳遞正規表示式的過程中似乎會默認它的型態為String,例如:
var reg = /\S/; //此時還是正規表示式

var str = "kevin";

check(reg, str); //丟進function後就變成String了

這時在check()中的reg.test(str)就會失效

經過survey後發現正規表示式其實是一個物件,在javascript中我們可以這樣來定義他:

var patt=new RegExp(pattern,modifiers);

//or below:

var patt=/pattern/modifiers;(pattern,modifiers);

實際的使用方法可以參考w3school的教學:
http://www.w3schools.com/js/js_obj_regexp.asp

所以在開頭提到的例子中我們可以先在check()的scope裡以str為變數新增一個正規表示式,如下:

var pat = new RegExp(reg);

alert(pat.test(str));

這樣就沒問題了~ :)


About "undefined"...

今天也遇到了讀取物件不存在的attribute會傳回undefined的問題,因為我們事先並不知道這個attribute是不是被建立了,所以我們可以用
typeof(obj.attr)=="undefined";
來判斷~



2012年8月24日 星期五

[宅] javascript筆記(2) - JSON, Object, String


要在javascript下建立一個obj只需要用以下的方法:
var obj = {key1:value1, key2:value2};


可以用"obj.key"的方式取得obj中的attribute,例如:
alert(obj.key);


有時候為了試用別人寫好的lib,會需要JSON格式的data,可以用字串轉JSON的方式人工產生,像這樣:
var value = 3;
var data = "{key:value}";//string
var json = eval('(' + data + ')');//json

這樣一來當你執行
alert(json.key);
就會跳出"3"了,如果希望以字串的方式寫死在value裡,可以用下的方式:
var data = "{key:'Hello JSON!'}";//string

至於JSON轉成object的方法,我們可以用jquery提供的parseJSON
var obj = jQuery.parseJSON('{"name":"John"}');



2012年8月6日 星期一

[宅] javascript 筆記(1) - 關於input標籤


一般來說,要取得<input>標籤內的輸入值,幾乎都可以透過jquery所提供的.val()來取得,其中select以及text類別的內容取得都還算容易,只要丟入id即可,但在type=radio的情形下就稍微比較特殊,要透過以下的方式來取得。
舉例來說:

<input name="StationName" type="radio" id="lbf_0" value="0" />

<input name="StationName" type="radio" id="lbf_1008" value="1008" />

<input name="StationName" type="radio" id="lbf_1011" value="1011" />

<input name="StationName" type="radio" id="lbf_1015" value="1015" />

<input name="StationName" type="radio" id="lbf_1017" value="1017" />

<input name="StationName" type="radio" id="lbf_1025" value="1025" />

<input name="StationName" type="radio" id="lbf_1319" value="1319" />

<input name="StationName" type="radio" id="lbf_1120" value="1120" />


因為每個選項都有自己的id,所以在selector的設定上就要費點心思,在這個情形下我們能用以下方式解決:

$("input[name='StationName']:checked").val();

這邊要注意的是,如果在selector中沒有加上"checked"這個條件,val()回傳的會是他所找到的第一個選項的value,而不是真正被check的選項。