over 3 years ago

紀錄首次使用時,記下的一些筆記、一些坑。

Reading Notes

  • 避免使用全域變數,非不得已,可以使用 shim 來建立你的全域變數

    Ideally the scripts you load will be modules that are defined by calling define(). However, you may need to use some traditional/legacy "browser globals" scripts that do not express their dependencies via define(). For those, you can use the shim config. To properly express their dependencies.

  • 以 module ID 命名。建議使用相對位置並去除副檔名 .js

    If a module ID has one of the following characteristics, the ID will not be passed through the "baseUrl + paths" configuration, and just be treated like a regular URL that is relative to the document:

    • Ends in ".js".
    • Starts with a "/".
    • Contains an URL protocol, like "http:" or "https:".

require.config

在開始使用 RequireJS 前,可以利用 config 先作一些定義。

paths

利用 paths 可以

  1. 替 script 建立 module ID ,可以簡化後續 import 使用的名稱。
  2. 縮短路徑名稱

例:

main.js
require.config({
    paths: {
      "js": "../js",  // 重新定義 js 所代表的路徑。

      "jquery": 'jquery/jquery.min',  // 以後 jquery/jquery.min 只要用 jquery 代替即可

      "bootstrap": "bootstrap/dist/js/bootstrap"
    }
});
shim
  1. export - 希望一個物件被視為全域變數時,可以利用 export 參數。

    main.js
    require.config({
        shim: {
            /*
            "Module ID": {
                export: "Object Name"
            }
            */
            sprintf: {
                export: "sprintf"
            }
        }
    });
    
    sprintf.js
    var sprintf = (function() {
            ...
    })();
    

    上例,只要是 sprintf module 被 import 的地方,其 script 裡面的 sprintf 物件會被視為全域變數。

  2. dependency - 對於無法獨自存在的 module,可以透過 dep 來設定。

    main.js
    require.config({
    shim: {
    /*
    "Module ID": {
    dep: ["Dependent Module"]
    }
    */

    bootstrap: {
    dep: ["jquery"]
    }
    }
    });



    上例,因為 bootstrap 是基於 jquery 的 module,所以必須特別設定相依關係。

    Some Gotcha

    1. 每一個 define 只會被執行一次。重複 define 只會得到上次的結果。
      所以要特別留意回傳的是類別還是實體。

    2. 同一頁的連續 script 要小心執行順序未必如您預期。例如:

      <script data-main="js/main" src="/static/require.js"></script>
      <script>
        require(["jquery"], function($){
            $('a[data-toggle=tooltip]').tooltip();
        });
      </script>
      

      通常會出現錯誤

      Failed to load resource: the server responded with a status of 404 (Not Found) 
      https://localhost:8080/js/jquery.js
      

      原因是 RequireJS 對於 script 的載入是非同步的,因此在 main.js 註冊的 jquery 名稱,會因尚未載入而無法被使用。他會認為這是一個新的 script 而直接使用路徑參照。

    參考資料

    1. RequireJS API
    2. jquery - Implementing AMD in JavaScript using RequireJS - Stack Overflow
    3. javascript - Require.js ignoring baseUrl - Stack Overflow
    4. Require.js 2.0 Shim Configuration
 
over 3 years ago

最近將自家一個案子的 Bootstrap 升級到 3。順便就把這些瑣碎的步驟整理並分類了一下。

General

  1. row 取代 row-fluid
  2. col-sm-* 取代 span*
  3. glyphicon glyphicon-* 取代 icon-*
  4. 移除 container-fluid 的區塊
  5. 預設 button style 需另外加上 btn-default
  6. 預設 label style 需另外加上 label-default
  7. 預設 alert style 需另外加上 alert-warning
  8. 改變 button size 對應: btn-[mini|small|large] → btn-[xs|sm|lg]

Form

  1. form-group 取代 control-group 以及 form-actions
  2. 依照長度不同,替 label 加上 col-sm-* class
  3. 依照長度不同,以 col-sm-* 取代 controls
  4. input 加上 form-control class
  5. 依照偏移量以及長度不同,以 <div class="col-sm-offset-* col-sm-*"></div> 包覆 action button
  6. .radio.inline 改為 .radio-inline 同理 .checkbox.inline 改為 .checkbox-inline
  7. input-group 取代 input-append
  8. input-group-addon 取代 add-on

附上一段簡單的 Diff Code

- <div class="control-group">
-   <label for="email" class="control-label">{{_("E-mail")}}</label>
-     <div class="controls">
-       <input type="text" id="email" name="email" required>
+ <div class="form-group">
+   <label for="email" class="col-sm-2 control-label">{{_("E-mail")}}</label>
+     <div class="col-sm-3">
+       <input class="form-control" type="text" id="email" name="email" required>
      </div>
  </div>
- <div class="form-actions">
-   <button type="submit" class="btn btn-primary">{{_("Login")}}</button>
-   <button type="button" class="btn">{{_("Cancel")}}</button>
+ <div class="form-group">
+   <div class="col-sm-offset-2 col-sm-3">
+     <button type="submit" class="btn btn-primary">{{_("Login")}}</button>
+     <button type="button" class="btn btn-default">{{_("Cancel")}}</button>
+   </div>
  </div>

Modal

  1. 內容主體外,額外包覆 2 層 div,分別是 div.modal-dialog 以及 div.modal-content
  2. modal-header 裡面的 h3 改為 h4,並加上 class modal-title

Caveat

  1. 彩色 Badge 已從 Bootstrap3 移除

參考資料

  1. Upgrade Bootstrap 2 HTML to Bootstrap 3
  2. Twitter Bootstrap
 
over 3 years ago

Python logging 系統主要分成三大部分:

  1. Logger - 觸發訊息紀錄。
  2. Handler - 處理訊息的顯示/儲存。
  3. Filter - 過濾訊息。

除此之外,還有一個 Formatter,用來排版訊息的顯示/儲存的格式。

簡介

Adapter 是包在 Logger 之上,在觸發訊息紀錄時,對於訊息的內容本體做一個預先處理 (pre-processing),有點 Middleware 的感覺。

撰寫

繼承 logging.LoggerAdapter 後,覆寫 (Override) 自己的 process,最後回傳 msgkwargs

例子
class MyAdapter(logging.LoggerAdapter):
    def process(self, msg, kwargs):
        msg = "[Preprocessed] %s" % msg
        return msg, kwargs

使用方式

>>> my_logger = MyAdapter(logging.getLogger(""), {})
>>> my_logger.info("My first adapter testing.")
[INFO    ] 2014-02-17 14:46:34 (<module>) [Preprocessed] My first adapter testing.

此時,每當 exec_logger 觸發一個 log 指令(debug, info, error, ...),都會被你的 Adapter 預先處理,以上面的例子來說,每個 Message 都會被加上 [Preprocessed] 的標籤。

接收額外參數

我一開始在寫 Logger 是想到的問題是:是否能夠多帶入其他參數來處理?例如:

my_logger.info("This is an infomation", user="admin")  

一般的 Logger 在處理關鍵字參數時,僅僅接受 exc_infoextra。因此想要處理額外的參數,就需要透過 Adapter。

例子
class MyAdapter2(logging.LoggerAdapter):
    def process(self, msg, kwargs):
        if "user" in kwargs:
            user = kwargs.pop("user")
            msg = "%s(by %s)" % (msg, user)
        return msg, kwargs

實際實行的結果

>>> my_logger2 = MyAdapter2(logging.getLogger(""), {})
>>> my_logger2.info("This is my logging message.", user="jim")
[INFO    ] 2014-02-17 14:40:28 (<module>) This is my logging message.(by jim)

唯一要注意的是,自帶的參數都必須從回傳的 kwargs 拔除,因為其他參數是不被接受的。

參考資料

  1. Python Logging Adapter
 
over 3 years ago

簡單的紀錄 git-subtree 的使用方式。

使用方式

建立

建立 subtree 的情況主要分成下面 2 種:

  1. 從既有 module 從 project 中分離:

    1. 將目標資料夾分離,建立一個新的 branch。
      $ git subtree split --prefix=src/mod1 -b new_module
      
    2. 以 subtree 的方式,將新的 branch 合併進現有 branch。
      $ git subtree split --rejoin --branch=new_module --prefix=src/mod1
      
      這時候一個 subtree 形狀已經生成了。
    3. 將剛建立的 subtree 推送至另一個 Repo。
      $ git remote add common_repo git@github.com:lemonlatte/common_repo.git
      $ git push common_repo new_module 
      
  2. 匯入外部 module:
    首先,建立一個空的 mod1 資料夾,接著匯入外部的 repo 形成一個 subtree。

    $ git subtree add --prefix=src/mod1 --squash common_repo new_module 
    

Pull

每當 new_module 有了更新,如果要將新的部份加入我們的 source,只需要透過:

$ git subtree pull --prefix=src/mod1 common_repo new_module 

其中 new_module 可為 branch name 或是 commit number。

Push

當你 source 有任何變動且已經 Commit。如果變動的地方包含你的 subtree,你可以透過下面指令,將屬於 subtree 的差異,推送到 subtree 的 repo。

git subtree push --prefix=src/mod1 common_repo new_module

分析

紀錄一下,目前看到的優缺點。

優點

  1. 漂亮的將 commit 的差異拆開
    輕鬆、簡單的在一個 project 下,將 commit 中分屬不同 repo 的 code 拆解並推送到各自的 repo 。

  2. 不打斷既有工作流程。
    由於程式碼依舊存在於同一 project folder 下,對於非專門維護 subtree 的 programmer,只需按照原有的 git flow 繼續 commit 即可。

  3. 不會因為 subtree 的 repo 消失,而影響到既有 source code 的進行。

缺點

  1. 每個 subtree 都需要一個 Root 資料夾。檔案無法在散佈 Root 以外的地方。不過這也很合理,因為 subtree 基本上也是一個 git repo。

  2. 依舊需要 Push 多次。目前還沒有指令可以一次完成。

參考資料

多不勝數。如有需要請自行 Google。

 
over 3 years ago

今天遇到了 Sphinx 讀取有 decorator 的函數時,抓不到 docstring 的問題。原因出在寫 decorator 時沒有把函數特徵保留給要回傳的的函數。

因為 decorator 的原理是 my_function = my_decorator(my_function)。因此得到的函數特徵是 my_decorator 這個函數的,而不是原先 my_function 的。解決方式是利用 functools 裡面的 wraps 函數將特徵轉移給新的回傳函數。

範例:

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

如果改完之後,docstring 還是沒有出現,有可能是你改的地方是 decorator 本身,Sphinx 誤以為現有函數並沒有修改,而沒有重新讀取,這時只要將 _build 砍掉重編就會出現了。

參考資料

  1. Python Sphinx autodoc and decorated members