Microsoft Sculpt Mobile Mouse and Mac

· Comments

tl;dr: Everything works properly, except the Windows Button. But you can remap it to Mouse Button 4 using KeyRemap4MacBook.

I just got a new mouse: Microsoft Sculpt Mobile Mouse. And because I only use Mac OS X, I didn't expect that all the features are available on OS X. The reason I choose Microsoft's mouse over Logitech's is because that many people reported that horizontal scrolling, or "spin", is not working at all on OS X.

Here is the test result for those who want to get one but don't know whether it works on your Mac.

關於 Ruby 的 autoload 與 Rails 的 autoload_paths 以及 reopen module / class

· Comments

最近在實作一個特別的需求,做了一個 gem 搞這種事:

  • 在 Gem 裡面, lib/models/post.rb 定義 Post < ActiveRecord::Base
  • 在 App 裡面, app/models/post.rb 打開 class Post 多寫一些 app-specific methods

然後就搞了三天搞不定。

具體的現象是:

  • 在 Gem 裡面,不論是使用 Kernel#autoload 還是 Rails 的 config.autoload_paths << 來做到自動載入,都無法在 App 改寫 Post class 。
  • 如果在 Gem 裡面不做 autoloading ,則 Rails 會去抓 App 裡面的 app/models/post.rb, which is not inherited from ActiveRecord::Base 。

之後試了繼承(很難搞)和 module ,最後是用 ActiveSupport::Concern 包了 module ,把 association 之類的東西寫在 included do 裡面,解決。

今天讀到這篇文章 Rails autoloading — how it works, and when it doesn't ,對於 Ruby 和 Rails 的 "autoload" 有粗略的瞭解了。簡單整理如下:

  • Ruby 的 Kernel#autoload 是告訴 Ruby runtime 「要找某個 constant 的時候,可以載入某檔案」,比較像是「登記」,在登記之後, Ruby runtime 若發現程式裡面有要用某個 const ,但沒有定義,就會載入該檔案,這是發生在「第一次使用」的時候,用第二次就不會觸發。
  • Rails 的 autoloading 跟 Ruby 的 Kernel#autoload 完全不一樣,實作方式是用 Module#const_missing :抓不到(const 在 runtime 沒定義)的時候才自動根據 constant 找檔名,例如 Taiwan::Taipei::SungShan 就是會找 taiwan/taipei/sung_shan.rb
  • 承上,「要去哪裡找檔案」這件事,是在 config.autoload_paths 設定的,這個 array 就是「要自動從檔案載入缺失的 const 的時候,就去依序搜尋哪些路徑」,類似 shell 的 $PATH 。如果檔案不存在,就會 raise NameError ;如果檔案存在,但 const name 跟所要找的不同,就會出現「Expected app/models/user.rb to define User」這種錯誤。
  • 承上,第一次載入完成以後,就可以在 Runtime 裡面找到,所以不會再度觸發 const_missing 來自動搜尋。

所以:

  • Kernel#autoload 不應跟 Rails 的 autoload_paths 混淆,要視為兩個完全不同的功能
  • 誰第一次載入誰算數, Rails 只在找不到該 const 的時候才會去 autoload_paths 搜尋
  • 所以,如果某個 const (class / module) 已經在 runtime 裡面定義了,那麼要在 Rails 裡面 reopen 它,就必須確定它一定會執行,例如 initializers 裡面,或是手動 require 它。如果是放在某個 autoload paths 裡面,例如 app/models/ ,則 Rails 並不會執行之,因為同名的 const 已經在 Runtime 裡面了。

這也就是為什麼會有「在 gem 和在 app 裡面,同名的 model class 是 mutually-exclusive,除非手動 require 才能改寫其內容」。也就是說,想要在 gem 裡面定義一個 model ,然後在 rails app 裡面 reopen 它,是不可能的,必須要手動載入它的 reopening。

說得更 general 一點就是:如果該 class / module 已經在 Gem 裡面載入,則要在 Rails 裡面 reopen 它,就必須放在 autoload_paths 以外的地方,並且手動 require 之。


該文很推薦一讀,除了詳細說明了 Ruby 和 Rails 的 autloading 機制,還提到一些陷阱,例如說 Rails 的 autoloading 其實不會理 Module.nesting (lexical context of current line) ,這樣子某些情況下會變成「第一次可以成功 autoload ,但第二次卻說 NameError 找不到 const」這種問題。

Ajax with FormData is Broken on IE10 / IE11 in Some Conditions

· Comments

If you've ever implemenetd sending form with Ajax, then, in some conditions, the request will be corrupted and the server may not be able to parse the request.

For example, there is usually an "I Agree to Term of Service" check box at the end of a registration form. I've made a jsbin sample which sends request to RequestBin so you can see the raw request without proxy software.

The form is simple, like this:

<form id="regform" action="/users" method="post">
  Username:
  <input type="text" name="user[username]" value="johnappleseed">

  Password:
  <input type="password" name="user[password]" value="s3cr37">
  
  Password Again:
  <input type="password" name="user[password_confirmation]" value="s3cr37">
  
  <!-- This input is our topic. -->
  <input type="checkbox" name="user[accept_tos]" value="1">
  I agree to Term of Service
  
  <input type="submit" name="commit" value="Sign Up">
</form>

And replace the submission of that form by Ajax. Here I use FormData to build XHR payload from a form element (see also: Using FormData Objects on MDN):

var form = document.getElementById("regform");

$(form).on("submit", function(e) {
  e.preventDefault();

  $.ajax({
    url:         form.action,
    type:        form.method,
    data:        new FormData(form), // build payload from a Form element
    contentType: false,              // tell jQuery not to adjust content-type
    processData: false               // tell jQuery not to convert raw data to string
  });
});

Let's leave "I Agree to Term of Service" unchecked and submit the form (via Ajax).

In Chrome and Firefox, the form submission will success, as expected. You can capture raw HTTP request by mitmproxy or Fiddler on Windows. Here is the request body I got:

------WebKitFormBoundaryoiKOSCNarm2sYgDv
Content-Disposition: form-data; name="user[username]"

johnappleseed
------WebKitFormBoundaryoiKOSCNarm2sYgDv
Content-Disposition: form-data; name="user[password]"

s3cr37
------WebKitFormBoundaryoiKOSCNarm2sYgDv
Content-Disposition: form-data; name="user[password_confirmation]"

s3cr37
------WebKitFormBoundaryoiKOSCNarm2sYgDv--

However in IE10 and IE11, the request body will be corrupted and the submission will fail:

-----------------------------7de3af25e0204
Content-Disposition: form-data; name="username"

johnappleseed
-----------------------------7de3af25e0204
Content-Disposition: form-data; name="password"

s3cr37
-----------------------------7de3af25e0204
Content-Disposition: form-data; name="password_confirmation"

s3cr37
-----------------------------7de3af25e0204
Content-Disposition: form-data; name="
-----------------------------7de3af25e0204--

See that unfinished name="? That is not encoded properly, and makes some HTTP server apps hard to parse the payload and may throw errors. In Rack (HTTP server app protocol for Ruby,) it would raise EOFError and the client will get 500 Internal Server Error.

在鼠鬚管 (Squirrel) 使用嘸蝦米

· Comments

使用嘸蝦米,證明

Screen Shot 2013-12-05 at 00.45.57.png

What?

鼠鬚管 Squirrel 是 Mac 上面以一個叫做「中州韻 RIME」為核心的輸入法,通常拿來當做漢語拼音輸入,而且拼音的對照表是用繁體編出來的,要打簡體的時候再轉成簡體,所以原生就支援繁體拼音,對於我這種需要繁體拼音的人來說非常方便。更多關於這套輸入法的介紹,可以參考〈推薦一個神級輸入法——Rime〉這篇文章。

注意到我稱中州韻為「核心」,Squirrel 實際上是 RIME 的 Front-End ,所以這次做出來的 RIME 輸入法設定檔,理論上可以用在他的 Windows 和 Linux 版 Front-End。Windows 的叫做小狼毫。不過我平常沒在用 Windows ,所以我沒用過。聽說目前還不支援 Windows 8 ……。

OAuth 2.0 Tutorial: Protect Grape API with Doorkeeper

· Comments

In this tutorial, I'll demonstrate how to protect the API with OAuth 2 protocol. The API is built with Grape, and mounted under Ruby on Rails.

This tutorial was originally written in Chinese, so if you know Chinese please read this: OAuth 2.0 Tutorial: Grape API 整合 Doorkeeper.

To protect the API with OAuth 2, we have to build the following things:

  • Resource Owner - The role who can grant authorization to the 3rd-party application, i.e. the User.
  • Authorization Server - Everything about authorization goes here, such as:
    • Clients - We need a basic CRUD interface to manage clients (applications).
    • Access Token (Model) - We need a Model to store Access Tokens.
    • Authorization Endpoint - Here to process Auth Code Grant and Implicit Grant flows.
    • Token Endpoint - The Access Tokens are actually issued here.
  • Resource Server - A location for applications to access, i.e. the API. Some APIs that need Access Tokens to access are called "Protected Resources."
    • Guard on Resource Server - To protect some APIs from accessing them without Access Tokens.

Most components are implemented with existing solutions:

  • Resource Owner (User) - Devise
  • Authorization Server (OAuth 2 Provider) - Doorkeeper
  • Resource Server (API) - Grape
  • Guard - Manually integrated Rack::OAuth2 into Grape.

Since doorkeeper_for from Doorkeeper can only be used in Rails, and Rack::OAuth2 is simply a Rack Middleware, so we have to mash them up manually. I've written a simple review for the current solutions: 〈Ruby / Rails 的 OAuth 2 整合方案簡單評比〉 (Chinese only; may be translated into English someday)

I've put the whole process in chitsaou/oauth2-api-sample repository. Each step has a corresponding step-x tag, for example the result of Step 1 is available at step-1 tag.


北京印象

· Comments

最近剛從北京回來,一來去 RubyConf China ,一來去遊玩。我記下這次北京行的印象,與 Conf 無關的部份。

機場印象

飛機降落在北京首都國際機機場的第三航廈 E 區。一下飛機,第一印象就是「高端大气上档次」(這句話要用簡體字寫比較有感覺),門面修整得非常漂亮,挑高的大廳讓人毫無壓力,萬年流行的玻璃帷幕包住整個航廈,跟桃園國際機場不是一個等級。中國政府應該是很重視門面,之後我注意到,連收費站都蓋得高端大气上档次。

入關的時候,大概是因為這裡專門停國際線班機,所以外國人的通道排得很長,中國公民的卻沒什麼人。懶得排隊,只好違背良心厚臉皮去排中國公民的通道……(其實服務人員說可以去排外國人那邊)。既然是國際線,怎麼不把中國公民的通道挪一些給外國人呢?我看到的是各一半。

入關不需要填入境卡,出境也不用填出境卡,不確定是不是台灣人才有的優待。不過這裡檢驗的人都是警察,一臉嚴肅貌,在台灣的卻是約聘公務員,且出境時的檢查也差很多,中國北京機場的海關會搜得很仔細,台灣桃園機場的安檢隨便搜,我皮夾手機沒拿出來嗶嗶嗶,安檢人員隨便掃了一下就照樣過關……。

入境的時候沒有拍到高端大气的照片,只在回程時拍了出境大廳的照片:

另外機場沒有把台灣歸類在「國內出發」裡面,我是有感到一點欣慰……。

OAuth 2.0 Tutorial: Grape API 整合 Doorkeeper

· Comments

這篇文章示範如何使用 OAuth 2 保護 API ,其中 API 是用 Grape 造出來的,掛在 Rails 底下。

英文版 / English Version: OAuth 2.0 Tutorial: Protect Grape API with Doorkeeper

整個實作流程會造出這些東西:

  • Resource Owner - 可以授權給第三方 App 的角色,也就是 User。
  • Authorization Server - 用來處理與 OAuth 2 授權有關的事務,像是:
    • Clients - 需要有 Clients (Apps) 的 CRUD 。
    • Access Token (Model) - 需要有個 Model 來儲存 Access Token 。
    • Authorization Endpoint - 這裡來處理 Auth Code Grant 和 Implicit Grant 。
    • Token Endpoint - 這裡來真正核發 Token 。
  • Resource Server - 給 App 存取的地方,也就是 API ,一部份需要 Access Token 才能存取的叫做 Protected Resource 。
    • Resource Server 上面的 Guard - 用途是「保護某些 API ,必須要帶 Access Token 才能存取」,俗稱保全。

本文使用這些套件來實作:

因為 Doorkeeper 的 doorkeeper_for 只能用在 Rails ,而 Guard 只是一個 Rack Middleware ,所以這裡要自己拼湊。詳情請見先前的文章 〈Ruby / Rails 的 OAuth 2 整合方案簡單評比〉

所有過程我都會放在 chitsaou/oauth2-api-sample 這個 repository ,各 step 有對應的 step-x tag ,例如 Step 1 完成的結果可以在 step-1 這個 tag 看到。