2016年9月12日 星期一

[fmpp] 利用 freemaker 進行 code gen

freemarker 是個在寫網站的時候遇到的 library

後來在找 template 工具的時候再遇到,也是覺得很有意思

比較了幾個 template 工具之後,發現 freemarker 的 template 跟 data(json) 比較符合我的想像

而且還有 CLI (command line interface) 工具,所以.... 就是他了!

這次主要是針對 CLI 部分來做簡單的說明

會需要 CLI 部分是因為最近開發專案已經不是習慣的語言 (Java --> .Net)

所以覺得工具應該不要跟語言本身有相依性了

這樣算起來工具學起來 CP 值也比較高吧

---

freemarker 的 CLI 是另外一個叫做 fmpp(FreeMarker-based file PreProcessor) 的東西

官方網址在這邊 (http://fmpp.sourceforge.net/)

設定起來也蠻簡單的

下載下來之後,設定好路徑可以指到 fmpp 指令所在就可以了

使用上只要在 sh 或是 cmd 裡面,包括有 config.fmpp (fmpp 設定檔案) 的資料夾中

輸入 fmpp 就可以了

至於細節在 fmpp 官網下載下來的 example 裡面有很完整的說明

---

那我到底是要說明什麼呢? 哈哈

這次做的東西簡單說是這樣

0. 當資料庫欄位需要異動
1. 在資料庫中查出 meta 並轉換成 json 格式
2. 樣板部分根據 meta 寫出 DAO/DAL 和 MODEL 層的程式
    (後續改版還增加了一些對日期格式 與 專案實際需求相關的修改)
3. 產生出來的 code merge 至目前程式中

這次的開發蠻有意思的

在整個操作裡面,發現原來整個開發流程重複的部分是可以被提出來

這時的思考層次就不太一樣

而且當我們是在維護系統的時候,這樣子的工具被撰寫與維護的價值就出現了

---

蠻有意思的

整個功夫沒有太大,但是當變更的時候 CP 值超高 (包括註解與一些麻煩的機制 都一次完成)

但是...

需要強調使用工具是為了滿足需求

寫 code gen 程式也是為了要滿足需求

需要小心不能離需求太遠或者是做太長遠的預先安排

例如,需求本來明明只需要做 DAL,卻花了一堆時間想連 CRUD 頁面都一併完成

例如,只是想關燈卻把整棟大樓線路都更新了... (最近看到的關燈梗)

(還需要好好練功啊... 加油...)

[D3] 這個假日玩了一下 D3 覺得可以有個系列文

這個週末試用了一下D3 (v4)

比起之前使用 Raphael Js 覺得蠻有深度的....

順便看了一下之前自己寫的文,還發現自己打文章實在是有夠糟糕

包括沒有條理還有根本就是邊想邊打...之類的...

覺得該利用 blog 好好反省一下

所以.... D3 系列文開始~~~

---

需要先澄清一下

目前沒有實際上使用這些 library 來做專案的經驗

都是用來寫自己開心的範例 跟 玩具

所以可能目標會有些不準確

也就是會根據自己的需要來寫囉...

2016年5月5日 星期四

[心得] 怎麼不導入單元測試


其實這個問題很久了,也研究了不少東西,更試著在各個專案裡面運行過
一開始傻傻地只想著要做單元測試,卻沒考慮那些重要那些不重要,提了不重要的東西出來還會被笑

弄清楚了優先度,可是時程就是只有兩個禮拜要做完,開發都沒時間了哪有時間寫單元測試??

就算有時間,還是可以找到別的事情讓單元測試不能進行 (插件、請假、別的專案、約會、改BUG、...)

更何況一般的專案進行,沒有寫單元測試還是可以開始做的
邊做邊談、邊談邊改、邊改邊上線、邊上線邊測,但必須要說這是很”有效”的方法
一邊可以處理客戶、一邊可以寫程式、一邊可以談需求、一邊測試,根本就到了平行化的一種極致

再者如果開始有機會開始,沒有經驗可能沒有辦法回答,該怎麼寫測試,該怎麼讓測試可以應付修改
怎麼寫才可以讓程式可以被測試、怎樣才能有效的檢核、UI怎麼測、DAL(Data Access Layer)該測嗎?
程式架構都不一樣怎麼測、需求變了變成要改兩次程式耶、都沒有時間開發了哪有時間寫測試?

不得不說,這些是一直發生在身邊的事情,而且真的是蠻嚴重的問題
----
我們要做的事情是怎麼讓系統的正確性提升,最重要的是明確的知道應有的系統行為是甚麼
在各種使用者的合理操作與不合理操作下,系統該有甚麼回應

這些分析完後就是檢核這些案例,腦子裏面檢核、邊開發邊檢核、開發完檢核、客戶檢核、使用者檢核

那麼多的檢核,如果不是依靠更強力的分析,大概是跳過一些檢核階段先不做,反正還有後面來節省時間 (?!?
----
會想寫這篇是因為
最近在邊開發一些工具,把一些周邊常做的事情半自動化
其實只是把常做的事情,透過一些 script 省下時間

從做工具省時間的角度來看... 突然有了一個合理的說法

完成正確的系統需要的不只是單元測試,而是怎麼讓所有繁瑣的檢核能更加快速度

以應付之前的所有理由,而且不是藉由花更多人力、減少測試、改動時程、減少功能來完成

ps. 工具在手不表示人人都是高手,做這件事情是需要練習的
支持測試更完整,支持善用工具加速測試,進行測試從你我做起

2016年5月2日 星期一

[JavaScript] 模組化規範 - CommonJS, AMD, CMD


[JavaScript] 模組化規範 - CommonJS, AMD, CMD

常見的作法... 我們把頁面當作一個一個的模組,借用 jQuery 或是原生的 dom library 進行初始化
jQuery(function{
    // init here
});
//or
document.ready(function(){
    // init here
});
至於功能模組化或是函式庫重用很少在我們的系統中出現
(根據本人不專業觀察)
一方面是業務邏輯等級的設計是真的很困難的,應該只有非常有經驗(重做很多次)的人有辦法處理
指的"絕對"不是只很會寫程式的人,而是很懂 Domain 而且會架構的人
因為分析階段完成後,並沒有有效的銜接設計階段方法,來確切地設計出有彈性地模組
這個情況並不是因為偷懶不進行設計
而是設計完不容易應付修改,尤其只能保證需求一定會改變...
不容易調整的設計,成為殺死團隊的第一把刀

一方面如果系統不是產品,只是一次開發進入維護階段的系統,其實靠人力跟運氣來處理就可以
誰敢重構已經能用的東西,一定是立馬被鞭 (想像是美麗的、現實是殘酷的)
所以這種設計一般發生在共用的字串處理、連線處理、非常常用的業務邏輯元件上
設計到業務邏輯等級,感覺還是在維護階段,針對常修改的功能進行來的合理些

再一方面在台(ㄍㄨㄟˇ)灣(ㄉㄠˇ)了解業務邏輯 、懂設計又那麼閒的工程師,應該蠻快就變成管理職...
(抱怨文完畢,正文開始)
------
CommonJS (http://www.commonjs.org/) 訂定了在非瀏覽器上面的許多 JavaScript 規範,其中正包括模組 (Modules) 。
而在瀏覽器裡面,使用 AMD 模式來處理,這邊主要參考 RequireJS (http://requirejs.org/docs/whyamd.html) 與 AmdJS (https://github.com/amdjs/amdjs-api/blob/master/AMD.md)

CommonJS - Modules/1.0 (http://wiki.commonjs.org/wiki/Modules/1.0)

基本上是這樣,完整點可以直接看定義,另外 1.1 與 1.1.1 版本看起來有再擴充,適用情境也更完整。
模組:使用 exports 變數承接定模組,一般使用檔案名稱當作模組 id
引用:使用 require 函數來指定引用的模組 id
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

AMD - Asynchronous Module Definition

Module
//Calling define with a dependency array and a factory function
define(['dep1', 'dep2'], function (dep1, dep2) {

    //Define the module value by returning a value.
    return function () {};
});
Named Module
//Calling define with module ID, dependency array, and factory function
define('myModule', ['dep1', 'dep2'], function (dep1, dep2) {

    //Define the module value by returning a value.
    return function () {};
});
Sugar1
define([ "require", "jquery", "blade/object", "blade/fn", "rdapi",
         "oauth", "blade/jig", "blade/url", "dispatch", "accounts",
         "storage", "services", "widgets/AccountPanel", "widgets/TabButton",
         "widgets/AddAccount", "less", "osTheme", "jquery-ui-1.8.7.min",
         "jquery.textOverflow"],
function (require,   $,        object,         fn,         rdapi,
          oauth,   jig,         url,         dispatch,   accounts,
          storage,   services,   AccountPanel,           TabButton,
          AddAccount,           less,   osTheme) {

});
Sugar2 Mixing
define(function (require) {
    var dependency1 = require('dependency1'),
        dependency2 = require('dependency2');

    return function () {};
});
當不是都透過 define 撰寫,還可以使用 requirejs.config() 來更有彈性的設定模組與模組間的 dependency
requirejs.config({

    //Remember: only use shim config for non-AMD scripts,
    //scripts that do not already call define(). The shim
    //config will not work correctly if used on AMD scripts,
    //in particular, the exports and init config will not
    //be triggered, and the deps config will be confusing
    //for those cases.
    shim: {
        'backbone': {
            //These script dependencies should be loaded before loading
            //backbone.js
            deps: ['underscore', 'jquery'],
            //Once loaded, use the global 'Backbone' as the
            //module value.
            exports: 'Backbone'
        },
        'underscore': {
            exports: '_'
        },
        'foo': {
            deps: ['bar'],
            exports: 'Foo',
            init: function (bar) {

                //Using a function allows you to call noConflict for
                //libraries that support it, and do other cleanup.
                //However, plugins for those libraries may still want
                //a global. "this" for the function will be the global
                //object. The dependencies will be passed in as
                //function arguments. If this function returns a value,
                //then that value is used as the module export value
                //instead of the object found via the 'exports' string.
                //Note: jQuery registers as an AMD module via define(),
                //so this will not work for jQuery. See notes section
                //below for an approach for jQuery.
                return this.Foo.noConflict();
            }
        }
    }
});

//Then, later in a separate file, call it 'MyModel.js', a module is
//defined, specifying 'backbone' as a dependency. RequireJS will use
//the shim config to properly load 'backbone' and give a local
//reference to this module. The global Backbone will still exist on
//the page too.
define(['backbone'], function (Backbone) {
  return Backbone.Model.extend({});
});

CMD - Common Module Definition(https://github.com/cmdjs/specification/blob/master/draft/module.md)

math.js
define(function(require, exports, module) {
  exports.add = function() {
    var sum = 0, i = 0, args = arguments, l = args.length;
    while (i < l) {
      sum += args[i++];
    }
    return sum;
  };
});
increment.js
define(function(require, exports, module) {
  var add = require('math').add;
  exports.increment = function(val) {
    return add(val, 1);
  };
});
program.js
define(function(require, exports, module) {
  var inc = require('increment').increment;
  var a = 1;
  inc(a); // 2

  module.id == "program";
});

UMD - Universal Module Definition

只是收攏上面那些定義不過相對彈性也較差
(function (root, factory) { 
    if (typeof exports === 'object') { 
        // CommonJS 
        module.exports = factory(require('b')); 
    } else if (typeof define === 'function' && define.amd) { 
        // AMD 
        define(['b'], function (b) {
            return (root.returnExportsGlobal = factory(b)); 
        }); 
    } else { 
        // Global Variables 
        root.returnExportsGlobal = factory(root.b); 
    } 
}(this, function (b) { 
    // Your actual module 
    return {}; 
}));

2016年2月8日 星期一

[javascript] 處理 js 的長時間工作

概念:
    javascript 是單執行緒來執行的,當事件被觸發後是會一個一個的執行,前一個沒有結束,後面的也不會執行

    常見的情況像是瀏覽器的 alert 阻擋住了後面的工作等等

衍生問題:
    使用 iframe 如何? 事件觸發順序是什麼 ?

-----
範例的第一個按鈕.... 真的會執行很久...

http://embed.plnkr.co/CNwaGKvRmIhALA0ZTYxb/

----
透過簡單的招式將工作切割成小工作來執行

var jobs = 200000;  // 總工作數
var partNum = 2000;  // 工作切割個數
var partSize = jobs/partNum; // 每個切割中的工作數
var iter = 0;  // 控制迭代的變數

2016年1月31日 星期日

[SQL] 三值邏輯,資料庫 null 在 where 中的運作

解釋下,上上禮拜新人遇到 oracle 裡面 '' 等價於 null,然後上禮拜又遇到下面這個問題

然後激發出我的雞婆個性 XDDD


遇到 null 的時候,我們的是非觀念.... 變得有點奇怪...

為什麼 in (null), not in (null) 都不給我回傳資料,搞毛啊 = =

我們一般的真值表是這樣的:


有一種叫做三值邏輯 Three-Valued Logic (TVL or 3VL),真值表是這樣的:

3VL 引入了"我不知道"的概念,這樣上面的真值表就很合理了!

NOT UNKNOW == UNKNOW
  "非"我不知道 當然還是 我不知道啊 XDD

TRUE AND UNKNOW == UNKNOW
  我吃飽了,但是我不知道你吃飽了沒,所以 "我們兩個都吃飽了"的問題 => 不知道

------

null 在資料庫中就是一個 UNKNOW 值的概念

一個地址欄位被填入了 null 時,正是代表了我不知道地址是哪裡的意思

而 SQL 的 WHERE 部分需要明確的 TRUE/FALSE 結果,來表示這筆資料該不該回傳

當查詢的結果是 UNKNOW 時,這筆資料是不會回傳

直接使用 =, <>, in, not in, exists, not exists 都是在做 TRUE/FALSE 的回答

所以常見的 id = null (UNKNOW)或是 id <> null (NOT UNKNOW)都是不會回傳資料的

我們需要表示不知道的時候,會需要明確地用 is null / is not null 將 3VL 轉回 2VL

------

以下上面第一張圖的解釋:

id in (null)
   id = null 結果是 UNKNOW 不是 TRUE 也不是 FALSE 不回傳

id not in (null)
   not (id = null) 等價於 id <> null ,所以結果是 UNKNOW  不是 TRUE 也不是 FALSE 不回傳

id in ('1', null)
   id = '1' or id = null,當 id 值是 '1' 是 TRUE,當 id 值不是 '1' 是 FALSE
   TRUE OR UNKNOW == TRUEFALSE OR UNKNOW ==  UNKNOW
   所以 id 值是 '1' 結果是 TRUE 會回傳,其他值得情況是 UNKNOW 不回傳

id not in ('1', null)
   not (id = '1' or id = null) 等價於 id <> '1' and id <> null
   參考真值表 TRUE AND UNKNOW == UNKNOWFALSE AND UNKNOW == FALSE
   所以就一筆資料都不會回傳

-----

報告完畢 應該是這樣沒有錯

2016年1月30日 星期六

[javascript] Hoisting & Scope

今天差點被奇怪的中文翻譯書給騙了 以為弄了那麼久竟然弄錯

只好寫下來紀錄一下

不過哥真的很認真

還有附圖 跟 live demo

----

Hoisting - 提升 - 意思是說 js 的變數宣告,其實會被默默地移至同一 scope 的開頭

因為是默默的,所以很容易有以下的誤解

http://embed.plnkr.co/jP72zKg7V3viYLAtGEeE/

var i = 0;

(function(){
    i = 5; // 很像是在操作上面那個全域的 i

    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行

    for(var i = 0 ; i < 10; i++){

    }
})()

alert(i); // ??? 是 0 還是 5 呢?


但是實際上會像是:

var i = 0;

(function(){
    var i;
    i = 5; // 很像是在操作上面那個全域的 i

    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行
    // 中間可能隔很多行

    for(i = 0 ; i < 10; i++){

    }
})()

alert(i); // 很明確是 0 沒錯了

所以如果可以,在 function 剛進來的地方宣告吧。
!!! 絕對不是在說多設定全域變數 !!!

----

Scope : 就是這個變數可以被人家使用的範圍

一般寫 c#, java 等語言,變數都是 block scope

意思是說從 宣告之後 到 目前所在的大括弧區間尾巴 (英文是一個 block ),都是可以使用的,一旦這個範圍還使用就噴錯了

但是 js 是 function scope 而且配合剛剛的 Hoisting 來說

可以使用的範圍是本變數所在的這個 function 從頭至尾都視做已經宣告

差別只是值被 assign 了沒有,undefined => assigned

然後有個比較特別的是直接宣告的 function

他是不管在哪兒宣告,都是在整個 scope 可見,就是被提升啦!!!!!!! (機車)

http://embed.plnkr.co/XgMeOjyD6AtMPtYdwayY/


範例一,只要宣告了那在前面使用這個變數是不會噴掉的!!!


範例二,就算是我們習慣的 block 也是一樣,超出了 block 值也還在喔


 範例三,那些在 function 之外的 access,噴!!! 直接就給你噴錯了!! 請小心

2016年1月6日 星期三

[Tech] 程式呼叫 Gmail 寄信

Gmail 寄信

最近使用 C# 寫 e-mail 的模組,想當然就是使用 gmail 做測試

不過發現一個新東西

原來針對低安全性的來源 gmail 會阻擋

需要去開權限才能使用

https://myaccount.google.com/security?pli=1#signin


直接進去登入和安全性,然後移到下方有個[允許安全性較低的應用程式]
預設是關閉的 記得打開測試完要再關起來