分類彙整:programming

EasyContact:撈Gmail跟Outlook信箱

最近工作上需要實作

「發信邀請Gmail好友」與「發信邀請Outlook好友」的功能,

發現兩者都支援OAuth2.0協定,進行認證與撈資料。

實作了一個library。目前只撈名字跟email地址

https://github.com/howtomakeaturn/EasyContact

希望能幫需要的人省下一些開發時間:)
ps: 本想支援Yahoo信箱,但發現它只支援OAuth1.0

整個流程比較難實作 orz..

還有需要支援什麼信箱請跟我說 :)

(Photo via  Sean MacEntee, CC licensed)

第三方API不支援localhost,怎麼辦?

在套用第三方API時,像是從Facebook撈資料、從Google撈資料、從Twitter撈資料,在驗證身份的流程中,通常第三方會要求你去登記、並設定一個redirect的網址。

以我的經驗來說,Facebook跟Google在這方面是比較友善的,網址可以設定為localhost,在本機開發與測試很方便。

但是像Yahoo跟Microsoft就比較嚴格:他們不接受localhost

這時候該怎麼辦呢?

我找到的solution是這個:

Ngrok

安裝與使用非常容易,我Ubuntu下載完之後,只輸入兩行指令

$ unzip /path/to/ngrok.zip

$ ./ngrok 80

就得到一個公開網址了。

Tunnel Status online
Version 1.3/1.3
Forwarding http://3a4bfceb.ngrok.com -> 127.0.0.1:80
Forwarding https://3a4bfceb.ngrok.com -> 127.0.0.1:80
Web Interface http://127.0.0.1:4040
# Conn 0
Avg Conn Time 0.00ms

他會將送到http://3a4bfceb.ngrok.com(每個人會有一個隨機網址)的request全部轉送到本機的server,實在非常方便。

(Photo via Ngrok

Facebook API拿不到使用者email?

許多網站實作會員系統時,會開發「用facebook登入」功能,或是註冊過程中套入facebook資料、減少使用者註冊的麻煩。

開發人員通常會覺得,至少拿得到email,對吧?

其實不然。就算使用者同意授權還是可能拿不到。

Facebook API有針對email欄位寫下這段:

This person’s primary email address listed on their profile. This field will not be returned if no valid email address is available.

所以,跟臉書拿資料時,不要用email欄位來判斷是否從facebook拿到資料了,用id欄位。也不要直接用email欄位去判斷使用者是否註冊過。

真的硬要寫入email有一個solution:臉書就算不給你email,若是有回傳username就將email存[username]@facebook.com、
連username都沒有就存[id]@facebook.com

臉書會將收到的信轉寄給使用者信箱。
注意:不是存進臉書收件匣,是轉寄到使用者的主要信箱。臉書政策改過。

http://www.theverge.com/2014/2/24/5443454/facebook-retires-its-email-service

注意:經我測試,有username就不能寄信到[id]@facebook.com,會失敗。

根據國外開發者的回報,他們的會員大約有0.1% – 2%有這個情形。

大家都以為至少能從臉書拿到email,這導致了一個國際級常見bug吧。

臉書工程師的解釋如下:

There are a number of circumstances in which you may think a user should have an email address returned but they will not. For privacy and security reasons it is not possible to elaborate on the precise reason any specific user’s email address will not be returned so please do not ask.

Some possible reasons:
No Email address on account
No confirmed email address on account
No verified email address on account
User entered a security checkpoint which required them to reconfirm their email address and they have not yet done so
Users’s email address is unreachable

可以參見以下文章的討論:

http://stackoverflow.com/questions/9347104/register-with-facebook-sometimes-doesnt-provide-email

https://developers.facebook.com/bugs/298946933534016

https://github.com/mkdynamic/omniauth-facebook/issues/61

最後再強調一次:不要以為一定能從臉書撈到email資料

(Photo via  marcopako, CC licensed)

框架,與物件導向程式設計。

最近分析Codeigniter框架的原始碼,開始思考一件事情:

一個框架的各個元件,是否應該要盡量能夠獨立於框架之外?

有人認為,框架的元件本來就附屬於框架之下,不需要也不應該獨立於框架之外使用。

我個人認為,只要是類別就應該盡量降低相依性,這樣重用性、擴充性、可讀性才高。

開始討論這個問題之前,我想先引用某位PHP programmer的一段話:

「所有框架都很爛!」(They All suck!)

http://www.phpclasses.org/blog/post/226-4-Reasons-Why-All-PHP-Frameworks-Suck.html

主要理由如下:

  • 同樣的code非必要地不斷重複執行
  • 需要太多相依的類別
  • 非必要的複雜解法
  • 重複做了伺服器提供的功能

他點出了框架的幾個缺陷。

我認為期待各元件能彼此獨立很好、不期待各元件能彼此獨立也沒錯。這似乎是一個個人喜好的問題,而沒有對錯之分。

下面是我認為正方以及反方的論點。

為什麼框架的元件應該可以搬出框架之外使用?

我認為,如果框架底下的元件相依性很低,那麼當開發者只喜歡其中幾個功能、而不喜歡整個框架的時候,便可以把元件搬到外面,另外拼裝或是手刻一個自用的framework。

若是框架能夠做到這樣,則上述PHP programmer的四項批評便幾乎不再是問題。

反之,像CI這種core功能嚴重相依的框架,則只能選擇全盤接受、或是不使用CI。

另外,也可以理解為某個功能在PHP的世界被創造過,你不應該重新創造、也不應該創造不能被重新使用的類別。這不正是物件導向程式設計的一個重點嗎?

為什麼框架的元件不應該搬出框架之外使用?

框架本身就代表了底下的眾多「元件」,以及某一種「使用這些元件的方式」。

以CI來說,許多元件使用了全域函數去讀取類別(load_class函數)、讀取config值(config_item函數)、記錄log訊息(log_message函數)、計算執行時間(benchmark類別),等等。

CI的元件本來就預設在CI之下使用,所以這樣設計確實很方便。否則為了能讓元件搬出去使用,要加上一大堆dependency injection技法、一堆地方要寫得比較general、彈性,也是麻煩。

話說如此,某些程度還是能同時兼顧所有優點的。例如Laravel的Eloquent ORM,在框架之內很優秀,搬出去用依然出色。

關於我對CI架構的批評,可參考:

http://howtomakeaturn.github.io/ci/common.html

http://howtomakeaturn.github.io/ci/output.html

(Photo via Akira ASKR, CC licensed)

Codeigniter原始碼分析

利用Codeigniter工作半年了,最近想深入了解CI框架的底層運作。

找了朋友一起,打算把整份source code讀過一遍,並且加上我們的一些解釋與批評。

http://howtomakeaturn.github.io/ci/menu

寫了幾份review之後,慢慢理解了一些對CI的批評是從何而來。
我目前看到最討厭的部份是core/common.php定義了大量的全域函數。
最可怕的是定義了load_class函數來負責載入絕大部分的類別。
這直接導致:
1、無法在建構式傳入參數
2、呼叫載入的類別要寫$URI =& load_class(‘URI’, ‘core’),真的很醜
3、所有相關類別因此都嚴重相依,無法把元件獨立出去在CI之外使用

另外,我還發現部份類別並沒有專注在「一個任務上」。

例如Output類別裡面有計算benchmark相關的code,違反了single responsibility principle,增加了維護與理解的難度。

也因此,整體說來,source code並不好讀。

目前只review了一小部份,還會繼續下去。

想收集更多意見並且與大家討論,希望有興趣研究codeigniter原始碼的朋友,一起加入我們:)

https://github.com/howtomakeaturn/Analyze-Codeigniter

(Photo via ecstaticist, CC licensed)

我2月12號到5月8號學到了什麼?(軟體篇)

工作幾個月之後我開始想:

大家都希望工作經驗越久、薪水越高,憑什麼?

年紀越大,實力一定越強嗎?

所以我比上個月強?我比昨天強?

到底強在哪?難道只能空泛的回答:我經驗更豐富了。

於是我就開始筆記,明確的記下來,為什麼我每天都比前一天強。

寫到今天約莫三個月了,決定打成「軟體篇」、「管理篇」兩篇文章。

跟各位分享這本流水帳。

2月12號

* 第一次用別張table的id當主鍵,以前都是新建一個id欄位當主鍵

* 第一次使用CodeIgniter內建的Test library

* 應用了Jamie Rumbelow的MY_Model跟它的relationship功能

* 我終於知道怎麼寫單元測試了!

* 第一次應用Mock up的技巧

* 第一次把dependency injection應用在舊的model

2月13號

* 替每個web app在server上建一個帳號有好處。改root密碼時就不用全部shut down了。

* 單元測試真不錯。你替model加新功能,接著加新test,然後你可以確定全部功能運作正常

* 學會怎麼用JavaScript驗證input是整數來達到不錯的User Experience。

* 很流暢的完成的一段複雜的交易系統。

* 在需要同時開發新功能、修bug、更新美工的情況下,用git的branch做了良好的管理。

2月14號

* 操你媽的Bitbucket居然會掛站。所以我學會如何在windows上用AppServ, WINSCP, GitShell, Putty, LinuxReader達到類似Linux的開發環境了。

* 媽的PHP居然病毒般吃光我Linux資源。所以我學會recovery 模式、top還有kill指令了。

2月19號

* 在前一份php檔定義常數可以防止異常使用當前的php檔欸

* 知道php的singleton函式怎麼寫了

2月20號

* 實作一個簡易log系統

* 學會jeditable,還有一點小hack

* Restful的定義多理解了一些。

3月3號

* 原來瀏覽器在submit表單只支援GET跟POST

* 知道怎麼流暢處理exceptions或是errors了

* 自創一套方法,在model內應用validation library。(別人都在controller內用,我覺得醜死了。)

* 知道怎麼用salt跟hash更安全的處理密碼資料。

* 媽的用salt下去hash,丟失salt之後為什麼還是可以驗證?上一個工程師到底在寫殺小?

3月5號

* catch住exception,可以顯示錯誤訊息然後再丟出去。這樣不錯。

* 學到了第三方套件Mandrill還有它的設計架構。

3月6號

* 學會怎麼用phpmail透過gmail發信了

* 實作了「忘記密碼」的程序

3月8號

* 他媽的,mysql的float居然連1.3或1.4都存不好,幹。

* php有array_unique跟unset這兩個函式欸。糙,這語言真的沒什麼品味。

* 將simpletest導入CodeIgniter並且準備使用它了。

* 今天又碰了一次spark

3月10號

* 這輩子第一次去看Apache的server log來debug!

3月14號

* 理解到controller應該幾乎什麼都不做,但model也不能太大。所有class都應該要小!用library吧,不要出現God Pattern,不要出現mixin(商業邏輯與呈現混在一起)。每個class專注在一件事上!

3月17號

* 知道如何用table將email內容置中

3月21號

* 學會migration了

3月22號

* 會用simpletest了

* 更了解Test Driven Development 

4月3號

* 更懂abstract class是什麼了。(誰要是繼承我,就必須提供這些函式)

* 在Github上開了我生涯第一個issue

4月9號

* 客製化Exception有兩種:傳入code或直接繼承它

這樣理解它:

用code就是狂欠技術債(一堆沒整理的數字),但幾乎沒利息要還

用繼承的方式就是資產,但開發比較慢

4月10號

* 理解前輩的log library在寫什麼了

* 理解PHP set_error_handler跟set_exception_handler,還有CI如何組織它們了

4月18號

* 知道了CI, FuelPHP, Kohana, Laravel的歷史背景了 

4月23號

* 原來上傳檔案會被PHP暫存在tmp資料夾,程式跑完會自動刪掉。

* 而實作上傳功能只是把它從tmp移出去而已。

5月2號

* 第一次接觸Angular.js。酷斃了。但它好像很自成一套,而且跟jQuery不相容。

* 我發現我的JavaScript還是寫成procedural programming,並不是OOP或是functional programming!

 

 

(Photo via  Brady Withers, CC license)

離開JavaScript新手村:Module Pattern

最近在開發「按讚」功能,也就是類似對商品列表「按讚」或是「加入我的最愛」之類的功能。

(本篇不討論server side與database實作,僅討論client side的JavaScript)

按讚的對象是衣服,於是我先開份js檔:

// clothing.js

// 我知道直接寫整串程式邏輯不好,一大團難以理解
// 那我用function把各小功能包起來好了!

// 先替按鈕加上click event,點了之後就送出ajax,
// 把這個衣服加入我的最愛
function setupToLikeButton(){
    // some codes
    $('.to_like').click(function(e){
        // some codes
    });
}

// 再來替已經按讚過的東西加上click event,
// 點下去就送出ajax,從我的最愛移除
function setupLikedButton(){
    // some codes
    $('.liked').click(function(e){
        // some codes
    });
}

// 大功告成,寫個「初始化」功能
// 正式執行clothing.js這個偉大的模組
function registerLikeButtonEvent(){
    setupToLikeButton();
    setupLikedButton();
}

然後在所有需要「按讚」功能的頁面,加入這些程式碼:

<script src="/assets/js/jquery.js"></script>
<script src="/assets/js/clothing.js"></script>

<script type="text/javascript">
    $(document).ready(function(){
        registerLikeButtonEvent();
    });
</script>

寫完之後,再看了一眼,一股涼意上心頭,覺得這些程式碼真的很可悲。

  • 這只是把一串js code,用function隨便包一包、把功能隨便分開而已
  • 在所有需要「按讚功能」的頁面加上一行registerLikeButtonEvent(),感覺不是模組化(一點也不物件導向),依然停留在procedural programming(這邊執行一串code、再跳過去執行那邊一串code,有bug的時候光tracing code就累死人)
  • setupToLikeButton跟setupLikedButton可能會被誤用。應該禁止在registerLikeButtonEvent以外的地方執行(有點像private method)
  • 多了setupToLikeButton、setupLikedButton、registerLikeButtonEvent三個global function,感覺就是很不爽

我對這串code很不滿意、覺得應該有更好的pattern。

立馬上網買三本書

  • JavaScript 設計模式
  • JavaScript大全(第六版)
  • JavaScript:優良部分

其中「JavaScript 設計模式」果然是開卷有益。下面就是應用Module Pattern之後的寫法。

// clothing.js

// 先做出一個命名空間,讓變數名稱留在local
var LikeButtonModule;

LikeButtonModule = (function(){

    // 提示使用者這個模組跟jQuery有相依性
    if (!window.jQuery) { throw new Error("LikeButtonModule requires jQuery") }

    // this line declares the module dependency and
    // gives the global variable a local reference.
    // 這行可以增進效能
    var $ = window.jQuery;
    var _setupToLikeButton = function(){
        // some codes
        $('.to_like').click(function(e){
            // some codes
        });
    }// end _setupToLikeButton

    var _setupLikedButton = function(){
        // some codes
        $('.liked').click(function(e){
            // some codes
        });
    }// end _setupLikedButton

    // the public API interface
    return {
        initialize: function(){
            // initialization
            _setupToLikeButton();
            _setupLikedButton();
        }
    };
}());

然後在所有需要「按讚」功能的頁面,加入這些程式碼:

<script src="/assets/js/jquery.js"></script>
<script src="/assets/js/clothing.js"></script>

<script type="text/javascript">
    $(document).ready(function(){
        LikeButtonModule.initialize();
    });
</script>

各位覺得,有沒有比較模組化的感覺呢?

Q&A

Q1: 為什麼 setupToLikeButton函式前面多了底線?

只是命名慣例,提醒developer它有private性質。

Q2: 那setupToLikeButton為何會得到private性質?

因為它是function內的一個var,被限定在function的closure內,所以function以外的地方不能執行它。

Q3: LikeButtonModule只有提供一個initialize函式給別人使用?這什麼爛模組?

等到「按讚系統」需要的功能更複雜、更豐富時,可以在最後return的物件內定義模組公開API,到時這個pattern會看起來很完整很漂亮。

Q4: 我看到一個很怪的文法 (function(){}()); 請問它是什麼?

那是JavaScript的立即函式。簡單來說就是定義一個function然後馬上執行它。注意這個函式最後return一個物件,而這個物件也就是這個模組的公開API介面。

(Photo via  Alison Christine, CC licensed)