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 頁面都一併完成
例如,只是想關燈卻把整棟大樓線路都更新了... (最近看到的關燈梗)
(還需要好好練功啊... 加油...)
2016年9月12日 星期一
[D3] 這個假日玩了一下 D3 覺得可以有個系列文
這個週末試用了一下D3 (v4)
比起之前使用 Raphael Js 覺得蠻有深度的....
順便看了一下之前自己寫的文,還發現自己打文章實在是有夠糟糕
包括沒有條理還有根本就是邊想邊打...之類的...
覺得該利用 blog 好好反省一下
所以.... D3 系列文開始~~~
---
需要先澄清一下
目前沒有實際上使用這些 library 來做專案的經驗
都是用來寫自己開心的範例 跟 玩具
所以可能目標會有些不準確
也就是會根據自己的需要來寫囉...
比起之前使用 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.jsvar add = require('math').add;
exports.increment = function(val) {
return add(val, 1);
};
program.jsvar 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 () {};
});
Sugar1define([ "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 Mixingdefine(function (require) {
var dependency1 = require('dependency1'),
dependency2 = require('dependency2');
return function () {};
});
當不是都透過 define 撰寫,還可以使用 requirejs.config() 來更有彈性的設定模組與模組間的 dependencyrequirejs.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.jsdefine(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.jsdefine(function(require, exports, module) {
var add = require('math').add;
exports.increment = function(val) {
return add(val, 1);
};
});
program.jsdefine(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/
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 == TRUE, FALSE 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 == UNKNOW,FALSE AND UNKNOW == FALSE
所以就一筆資料都不會回傳
-----
報告完畢 應該是這樣沒有錯
2016年1月30日 星期六
[javascript] Hoisting & Scope
今天差點被奇怪的中文翻譯書給騙了 以為弄了那麼久竟然弄錯
只好寫下來紀錄一下
不過哥真的很認真
還有附圖 跟 live demo
----
Hoisting - 提升 - 意思是說 js 的變數宣告,其實會被默默地移至同一 scope 的開頭
因為是默默的,所以很容易有以下的誤解
http://embed.plnkr.co/jP72zKg7V3viYLAtGEeE/
Scope : 就是這個變數可以被人家使用的範圍
一般寫 c#, java 等語言,變數都是 block scope
意思是說從 宣告之後 到 目前所在的大括弧區間尾巴 (英文是一個 block ),都是可以使用的,一旦這個範圍還使用就噴錯了
但是 js 是 function scope 而且配合剛剛的 Hoisting 來說
可以使用的範圍是本變數所在的這個 function 從頭至尾都視做已經宣告
差別只是值被 assign 了沒有,undefined => assigned
然後有個比較特別的是直接宣告的 function
他是不管在哪兒宣告,都是在整個 scope 可見,就是被提升啦!!!!!!! (機車)
http://embed.plnkr.co/XgMeOjyD6AtMPtYdwayY/
範例三,那些在 function 之外的 access,噴!!! 直接就給你噴錯了!! 請小心
只好寫下來紀錄一下
不過哥真的很認真
還有附圖 跟 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 值也還在喔
2016年1月6日 星期三
[Tech] 程式呼叫 Gmail 寄信
Gmail 寄信
最近使用 C# 寫 e-mail 的模組,想當然就是使用 gmail 做測試不過發現一個新東西
原來針對低安全性的來源 gmail 會阻擋
需要去開權限才能使用
https://myaccount.google.com/security?pli=1#signin
直接進去登入和安全性,然後移到下方有個[允許安全性較低的應用程式]
預設是關閉的 記得打開測試完要再關起來
訂閱:
文章 (Atom)