CI/CD 環境建置筆記(一) - 在 windows 安裝 Jenkins

前情提要

操作記錄

  1. 下載並安裝 Jenkins(記錄版本為 2.32.1)

  2. 連線 localhost:8080,會要求輸入Administrator password

  3. 安裝 Plugins(這裡選擇預設)

  4. 安裝畫面

  5. 建立管理者帳號密碼

  6. 登入後,管理 Jenkins > 設定系統

    1. 將執行程式數量設定為 1 (安裝完預設為 2,其實可以不用修改)
    2. Shell 設定為 C:\Windows\system32\cmd.exe
  7. 建立第一個作業,選擇新增作業 > 輸入作業名稱 ,選擇「建立多重設定專案」

  8. 執行一次建置,這個步驟是為了產生 work space 。
    work space 路徑大致如下 .\Jenkins\workspace\Project name

  9. 執行 console 並切換路徑至 work space

  10. 將 Openshift 上的 nodejs 應用程式 repo 設為 remote
    >git remote add prod ssh://5**********************@nodejs-youraccount.rhcloud.com/~/git/nodejs.git/

  11. 回到 Jenkins,作業 > 組態 > 建置 > 新增「執行 Windows 批次指令」

    1
    2
    3
    REM 測試
    whoami
    git push prod HEAD^:master
    1. 額外處理事項:
      直接在 Jenkins server 發 pr 給 openshift 時,發生 503 錯誤。
      使用 ssh 登入 openshift 看 log ,發現
      Node Sass does not yet support your current environment 錯誤
      必須登入執行以下語法修正模組問題 npm rebuild node-sass
  12. 建置作業,會得到錯誤訊息

1
2
3
4
上略...
19:44:46 Host key verification failed.
19:44:46 fatal: Could not read from remote repository.
下略...

原因:主機密鑰驗證失敗,這個錯誤的意思是我的 Jenkins Server 主機並不認得遠端的 Openshift Server 的 host key,主要的原因是 Jenkins Service 在執行的身份是 NT AUTHORITY\SYSTEM,由於我已經有合適權限的帳號,所以只需要切換執行 Jenkins Server 的身份即可。

如果你尚未建立遠端 ssh 連線的存取權限,或是對 ssh 連線不熟悉,可以參考文末的參考聯結。

參考

(fin)

CI/CD 環境建置筆記 - 前言<目的>

現有架構

現有架構

版本控制

  1. Github
  2. Openshift

Openshift 是實際線上服務所在,也可以稱作是我的產品(Product),
Github 則是單純的負責版本控制。

開發流程

作為獨立開發的專案,這個網站只是我很單純的自我介紹、練習(KATA)、學習與分享的集散地。
原本的開發流程很單純,如上圖。完成的 CODE 同步到 GITHUB 後也同步上 Openshift ,
這樣的流程雖然簡單,但是每次都要人工執行,因為其執行順序,其實都隱含著風險,
例如:上錯版本或是產品未進版控等等…,
而當進一步想要執行建置、編譯 、套件管理、測試、分析與產生報表時,
產品與源碼將會產生差異(ex:產品會包含壓縮的檔案而源碼則是未壓縮的檔案)

缺點

  1. 在這樣的架構上容易不小心發佈還在開發中的功能。
  2. Prod 的來源就是程式碼,無法確保與版控一致,如此就喪失版控的用意。
  3. 難以擴展其他功能,如:建置、編譯 、套件管理、測試、分析與產生報表

目標

目標
如圖所示,
第一步 Jenkins 要將原始碼自 Github pull 下來
第二到第 N 步 Jenkins 要執行建置、測試、coding style 的檢查、壓縮…等等。
最後一步 Jenkins 將會將程式碼發佈到 Openshift 上面。
中間的步驟先跳過,之後再一一補上。
所以第一目標很簡單,
從 Github Pull 再 Push 到 Openshift。

(fin)

[記錄]VS2015 StyleCop 誤判SA0102

環境

  • 作業系統:Windows 10
  • 開發工具:Visual Studio 2015 (Professional ver14.0.25431.01 Update 3)
  • StyleCop 4.7.50.0

情境

當使用 C# 6 的INTERPOLATION STRING組合字串時,在 StyleCop 4.7 會誤判並回報 SA0102 的警告。

1
2
3
4
5
6
return new ExcelResult
{
Data = exportData,
FileName = $"{shopId}_Code_{DateTime.Now.ToString("yyyyMMddHHmm")}",
SheetName = "ECouponCode"
};

修正目標

  1. 將 StyleCop 4.7 更新至 StyleCop 5.0
  2. 編輯客製化的 StyleCop Rules(直接將 4.7 的設定檔覆蓋 VS 會報錯)

修正記錄

  1. 移除 StyleCop
    控制台 > 新增移除程式

  2. 移除 Visual Studio 上的 StyleCop
    工具 > 擴充功能和更新

  3. 自你的程式碼移除所有對 StyleCop 的參考
    我的程式碼中並不包含對StyleCop的參考,故略過此步驟

  4. 搜尋StyleCop*.dll 並且移除(非必要)

  5. 重新安裝 StyleCop Visual Studio Extension 1. 安裝完成檢視安裝記錄檔

    2.  可以透過安裝記錄檔取得安裝路徑
    

  6. 客製修改 StyleCop 設定檔
    5-2 的步驟可以找到Settings子資料夾,內含預設 StyleCop 設定檔,
    複制這個檔案到上一層,重新命名為Settings.StyleCop,
    透過StyleCop.SettingsEditor開啟Settings.StyleCop調整設定值。

參考

(fin)

[閱讀筆記] 把時間當作朋友

零、困境

✖ 時間無法管理 能管理的只有你自已。

一、醒悟

  • ✖ 用思考控制思考
  • ✖ 心智:就是心靈的智慧,用理性處理感性,控制情緒與慾望。
  • ✖ 人會在相同的情況下作出完全相反的結論

…歷史上也常常出現這種情況——其實目的都是“為了人類更好的明天”,可偏偏出現了對立的兩派人,
他們為了原本一模一樣的理想爭執不休,甚至“拋頭顱”、“撒熱血”,犧牲幾代人的福祉。

比如,所謂“水火不相容”的“社會主義陣營”和“資本主義陣營”。
看穿這一切,擺脫自己的局限,需要心智的力量。

二、現實

  • 速成絕無可能
  • 交換才是硬道理
  • 完美永不存在
  • 未知永遠存在

未知分為兩種:一種是永遠不能解決的,另一種是在可預見的未來也許能夠解決的。
✖ 吾生也有涯,而知也無涯。以有涯隨無涯,殆已!已而為知者,殆而已矣!…指窮於為薪,火傳也,不知其盡也。–莊子。養生主
✖ 站在巨人的肩膀上

  • 現狀無法馬上擺脫

接受現狀才是最優策略——有什麼做什麼,有什麼用什麼;做什麼都做好,用什麼都用好。
不要常常覺得苦(這會讓人忍不住顧影自憐,浪費精力與時間),而要想辦法在任何情況下找到情趣——
——快樂是一種本事

心懷“夢想”的時間越長,它的沉沒成本就越高。

✖ 積沙成塔,滴水穿石,時間可以累積,也可以消耗。

  • 與時間做朋友

與時間做朋友的方法很簡單:用正確的方法做正確的事情。

儘管現實總是如此難於接受,堅強的你卻應該坦然。以上提到的種種現實,包括“速成絕無可能”、“只有付出才有收穫”、“完美永不存在”、“未知永遠存在”、“現狀無法馬上擺脫”,都既清楚又簡單,你必須要接受——不僅要接受,還要牢記;不僅要牢記,還要堅信,不容半點動搖。最好時常把自己的一些念頭記下來,然後與這幾條現實對照,看看它們是否與這些現實相符。

時間是現實的人的朋友,是不現實的人的敵人。時間不是故意要這樣做,只不過事實如此。

✖ 正確的事,正確的做 ; 首先要學會判斷什麼是正確的事。

三、管理

  • 估算時間

分辨任務的屬性——它是熟悉的還是陌生的呢?

對學習來說,任務“陌生”的可能性更大,因為學習本身是一個探索未知的過程。完成學習任務常常需要花費比我們想像中多得多的時間。對工作來說,任務“熟悉”的可能性更大。因為工作本身是一個應用已知的過程。

✖ 軟體工作的這個行業,很有可能是大多數是未知的情況。

  • 及時行動

接受任務之後,什麼時候開始執行才好呢?比“越早開始越好”更切實的答案是“現在就開始”。

✖ ASAP 不是一個好答案 NOW 才是。

✖ 不求有功,但求無過是種慢性自殺,滴水穿石—會消磨掉你的累積。

  • 直面困難

效率低下的根本原因是什麼?答案是:迴避困難。
……
……這些問題都源自同一個習慣:專做簡單的,迴避困難的。

  • 關注步驟

所謂“三思而行”在我看來就是指做任何事情之前都要考慮相關的 3 個方面:內容(What)、原因(Why)、方法(How)。任何任務都起碼具備 3 個屬性:何事(What)、何因(Why)、何法(How)。

不停地細分、拆解任務,而且越具體越好,直至每個小任務都可以由一個人獨立完成。

例子

擴充詞彙量 → 托福詞彙 → 托福核心詞彙 →21 個單元 → 每個單元大約 100 個單詞 → 一個單元分 2 次完成……
▷ 先嘗試做一兩個階段,測量一下完成一個階段需要多長時間。
▷ 按照測量的結果製作一個時間表,把其餘若干個階段所需要的時間填寫完整(最終總是需要做一些調整的)。
▷ 背單詞需要重複,所以,每 3 個階段過後要留出與完成 1 個階段相等的時間去複習。這就意味著一共需要花費完成 56(42+14)個階段的時間。
▷ 每完成總任務的 1/3,就增加與完成 1 個階段相等的再复習時間。這就意味著一共需要花費完成 59(56+3)個階段的時間。
▷ 學習過程中可能需要多次快速重複記憶,每次可能相當於完成 3 ~ 5 個階段所需要的時間。由於熟悉程度的不斷增加,每次重複記憶所需要的時間會越來越短,所以,預計進行 3 次重複記憶需要相當於 10 個階段的時間。這就意味著一共需要花費完成 69(59+10)個階段的時間。

完成每個階段的具體步驟。

▷ 每天早晨騰出一點時間。
▷ 把前一天背過的單詞朗讀至少 2 遍。
▷ 聽錄音,跟讀當天要背的單詞 3 ~ 5 遍,主要關注發音、拼寫,順帶看看詞義,能記多少就記多少,不求速成。
▷ 上午利用閒暇時間通讀詞彙列表,並反复閱讀例句。
▷ 下午用專門的時間把當天要背的單詞集中背 2 ~ 3 遍。可以一邊讀,一邊抄,一邊背,不要只是坐在那里呆呆地盯著詞彙看。
▷ 空閒的時候反复聽當天要背的單詞,重複次數越多越好。
▷ 晚上睡覺前複習當天背的單詞。

  • 並行串行

✖ 拆分細項,尋找並行的可能。

華羅庚先生曾經用燒水泡茶為例說明過這個問題。

燒水泡茶:參見《統籌方法平話及補充》,華羅庚著,中國工業出版社,1966。
▷ 辦法甲:洗好水壺,灌上涼水,放在火上。在等待水開的時候洗茶壺、洗茶杯、拿茶葉。等到水開,泡茶喝。
▷ 辦法乙:先做好一些準備工作,包括洗水壺、洗壺杯、拿茶葉。一切就緒,灌水、燒水,坐待水開了泡茶喝。
▷ 辦法丙:洗淨開壺,灌上涼水,放在火上,坐待水開。水開了之後急急忙忙找茶葉、洗茶壺、茶杯,泡茶喝。

*✖ 分別為聖的時間片*

一個處理器在一個時間段內其實只能做一件事,因為它只有一個個體、一個時空。而多任務操作系統把一個長時間段劃分成很多短小的時間片,每個時間片只讓處理器執行一個進程(Process)——儘管同時可能有多個進程需要處理。在第一個時間片裡,操作系統讓處理器處理 A 進程;時間片的時間用完之後,無論 A 進程處理到什麼程度,都要被“掛起”(即,A 進程這時不能再佔用處理器資源——儘管它還是被允許使用計算機的其他資源,如內存、磁盤、屏幕輸出等);在第二個時間片裡,處理器處理的是 B 進程,時間用完之後,B 進程將與 A 進程一樣被中途“掛起”;

  • 感知時間

逃避責任就會帶來輕鬆,可那恰恰就是“生命中不能承受之輕”啊!

有的時候「逃避雖可恥但有用」,學會判斷什麼是可以逃避的事,什麼是不可逃避的事。

學著做每天的“事件日誌”(Event Log)。除了自己經歷的事件之外,
一概不記,而且盡量不記感想,不記感受,只記錄事件本身。

…記錄過程要比基於結果的記錄更為詳盡

例子

烏里揚諾夫斯克。一九六四年四月七日。分類昆蟲學(畫兩張無名袋蛾的圖)——三小時十五分。鑑定袋蛾——二十分(1.0)。

附加工作:給斯拉瓦寫信——二小時四十五分(0.5)。

社會工作:植物保護小組開會——二小時二十五分。休息:給伊戈爾寫信——十分;
《烏里揚諾夫斯克真理報》——十分;列夫·托爾斯泰的《塞瓦斯托波爾紀事》——一小時二十五分。

基本工作合計——六小時二十分。

✖ 簡單的分類 + 事件 + 費時,最後再合計起來。
(其於事實)條列式的記錄法,與時間調色盤(預定)的方法不太一樣。
你可以預定你要作的事,可是你無法正確的估時 ; 這是事實,請面對這個事實。
輔助上分別為聖的時間切片或許可以有所幫助

  • 記錄開銷
    練習中

  • 制訂預算

✖ 先養成記錄的習慣,再養成制定預算的習慣
✖ 只列事情,重要/不重要

每個人都喜歡做有趣的事情,做的時候往往並不關心這件事到底有沒有用。可是,有趣的事不見得有用啊!

養成任何一個哪怕很小的習慣都需要掙扎。然而,貌似痛苦的掙扎過程,在將來的某個時刻終將變得其樂無窮。

  • 計劃

計劃成功的前提:目標現實可行

我們不是計劃著失敗,而是失敗地計劃(People don’t plan to fail, they fail to plan)。

✖ 簡單的判斷目標是不是現實可行

證明我的目標現實可行的方法比較簡單:

  1. 已經有人做到了。
  2. 我與那個人沒有太大的差距。

長期計劃是需要通過實踐才能習得的能力

沒有人能給我做職業規劃。…生活本身充滿了意外,並且,總是意外到無以復加的地步。

✖ 先從記錄開始,持續一周,繼續保持記錄下試著開始分配時間。
✖ 再試著從周計劃開始作規劃,練習並且保持。

計劃固然重要,行動更為重要。

任何事情,都可能經歷相同的過程:逐步熟悉,小心摸索,失敗、失敗、再失敗,
認真反思,捲土重來,直至成功。而最初,在我們對任務連基本的認知都沒有的時候,
制定出來的計劃十有八九隻不過是空談。

  • 列表

  • 流程

✖ 預想各種可能的情境,不用一次到位,持續整合的重點在持續。

範例:

▷ 確定對方最可能方便接電話的時間(難以確定的時候先發短信詢問)。
▷ 在撥打電話甚至發短信詢問對方是否方便之前準備好一切計劃溝通的內容,作好檢查列表放在手邊,確保溝通過程中不會遺漏要點。
▷ 通話前把重要信息整理成電子文本,在通話中做必要的更新,通話結束後,馬上通過電子郵件將備忘發送給對方。
▷ 若對方沒有接電話,則給對方發署名短信告知詳細事由, 並做好記錄,防止自己遺忘此次溝通任務。
▷ ……

  • 預演

準備不足,所以害怕。

✖ 類似一種沙箱測試,讓錯誤發生在測試階段。

  • 驗收

向自己提出一個問題並要想辦法回答清楚:“怎樣才算’做好’?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
本篇心得: 這是需要實際操作的一篇,仍在實踐中。
首先是記錄自已的時間花在哪裡?
再來是制定時間的預算,分別出神聖的時間去累積一些東西(ex:學習英文、跑步);
當習慣養成,就不再困難了,而是生活的一部份。
同時注意工作的流程,
1. 問自已What Why How ?
2. 儘可能拆解工作流程的步驟到最小化,建立列表。
3. 常常發生的工作要設想各種情境,建立 SOP 以縮短時間。
4. 陌生的工作可以透過預演來試錯。
5. 一定要驗收。
6. 面對困難與失敗。
7. 及時行動!
8. 及時行動!!
9. 及時行動!!!

四、學習

基本途徑

  • 體驗
  • 觀察
  • 學習

✖ 自由是可以被剝奪的。

經驗局限

日常生活中主要的“溝通障礙”本質上幾乎都是由於溝通雙方無法讓對方理解與他們的經驗相悖的知識或信息造成的。

長期的進化使人類到達了今天這個高度,但是每個人在出生的那一剎那,居然與其他動物站在幾乎同樣的起點上,心智要從零開始進化。

優秀的領導,能夠把人們帶到他們想去的地方;而卓越的領導,能夠把人們帶到他們應該去但是沒想過要去的地方
A leader takes people where they want to go. A great leader takes people where they don’t necessarily want to go,but ought to be.

牢記在這世界上確實存在“與現有經驗相悖的知識”,再把這句話變成經驗,用它去類比未知,而後投入大量的時間和精力去學習和掌握“科學方法論”,掙扎著進化成為真正意義上的“人”。

自學能力

  • 閱讀理解能力 ->檢索能力 -> 寫作能力 -> 實踐能力
  • 永遠保持開放的心態

最好固定一段時間來把那些目前暫時無法理解的、支持的、反對的、無所謂的論點和觀點記錄下來。對無法理解的,寫下自己當時的疑惑何在;對支持的,記錄幾個理由或者實例;對反對的,同樣記錄幾個理由或者實例;甚至對那些無所謂的,也記錄其原因。一個有著這樣良好記錄習慣的人會獲得他人無法擁有的處理信息和知識的能力——“反芻”。這種“反芻”能力是我們避免成為“選擇性輸入”受害者的重要保障。
在學習的時候,進展和時間的關係肯定不是線性的:所謂“一分耕耘,一分收穫”。這個關係曲線更可能是階梯狀的:學習過程中有很長時間一點進展都沒有,但從某一刻開始突飛猛進,而後又是長長的一段所謂的“平台期”。…只有經歷積累的過程,“量變到質變”的效果才會出現,才有可能突飛猛進。

五、思考

勤於思考

人都有大腦,閒置還是使用,是個問題。

  1. 獨立思考
  2. 思考任務的目標、實質、意義

很多人就是這樣,堅持拒絕思考,然後用天下最累的方式生活而不自知。大哲學家羅素曾觀察到這個現象,他為之奇怪並慨嘆,“ 很多人寧願死也不願思考 ”,然後戲謔道,“ 實際上,他們確實死得很快。”

Most people would die sooner than think — in fact they do so.
— Bertrand Russell,In The ABC of Relativity,1925,P.166

思維陷阱

概念不清和拒絕接受不確定性

  1. 概念不清

    學習任何知識最重要的一點,就是搞清楚它所有的基礎概念。

    讀教科書,要先把所有概念都記下來,暫時不懂的就死記硬背。把概念牢記於心,就可以通過以後的學習和實踐反複審視它,並形成透徹理解。

    本書強調“時間不可管理”、“我們只能管理自己”,並非咬文嚼字。“時間管理”和“自我管理”是完全不同的概念——焦點不同,方法不同,效果不同

  2. 拒絕接受不確定性

未知永遠存在。從本質上來看,不確定性和未知是一回事。

因果關係

基礎

外因會影響內因,內因同樣會影響外因。它們相互影響,互為因果。

舉一個特別好玩的現象為例:在某種意義上,學生的水平決定教師的水平。

這話並沒有說反。師生之間的有效溝通,肯定不僅是教師單方面的灌輸。越是用心的教師,越關注學生的反饋;越是用心的學生,越關注教師對他的反饋的反饋。顯然,這種溝通不僅是雙向的,還隨著雙方的用心程度而不斷增強。

這和下棋是一樣的道理。據說,棋藝到了一定程度,棋手就會不由自主地挑選對手,因為跟高手下棋就會進步,但反過來,與“臭手”交手多了,自己的手也會變“臭”……優質的學生,對他們的教師來說,不僅是令人愉悅的教學對象(學生一點就透,老師沒有不開心的),更重要的,他們還是對教師的挑戰——這些學生有著長期而且優秀的學習經驗,也因此擁有相對良好的判斷能力,隨時可能提出一般教師無法回答的問題。經過一段時間的積累,雙方都會因為教學和溝通發生巨大的進步——只要雙方都足夠優秀,足夠用心。

正在塑造我們的這個環境,也是我們自己(參與)塑造的

要做一個用心的人,要用心做事,因為這世界其實也是有“心”的

雙盲測試

註: 受測者不知道安慰劑的存在,測試者則不知道誰服用了安慰劑,單純以局外人的角度觀測記錄。

人群中也有差不多 ⅓ 的人更易受到(來自他人或來自自身的)心理暗示的影響。也就是說,他們更可能在服用安慰劑後病情真正好轉。

自證預言

莫頓教授用銀行擠兌的例子說明了自證預言的作用機理:

一家銀行本來運作得很正常,但不知什麼原因,出現了“這家銀行要倒閉”的流言。
流言越傳越廣,導致越來越多的人信以為真,有人為防意外而跑到銀行把自己的存款提走。
恐慌情緒蔓延,並且變得愈加真實,更多的人衝進銀行提走自己的存款……最終,擠兌發生了,銀行真的倒閉了。

一個原本並不存在的原因竟然“無中生有”變成了真正的原因。

心中恐惧失敗,才是失敗的原因

逆命題

原命題為真,它的逆命題不一定為真。
P 則 Q 等於 ~Q 則 ~P
但是不一定存在 Q 則 P 的關係
有 70%以上的人分不清楚原命題和逆命題之間的區別。

參見《認知心理學》(Cognitive Psychology: A Student’s Handbook,2000),邁克爾·艾森克(Michael W. Eysenck)、馬克·基恩(Mark T. Keane)合著。

✖ 心得: 人很好騙,至少 70%很好騙。

舉證責任

“舉證責任”是不對等的
爭論雙方的境況常常處於這種狀態:
▷ 其中一方肩負沉重的舉證責任(burden of proof)
▷ 另外一方則享有來自假設的恩惠(benefit of assumption)

既然你無法證明我是錯的,那麼我就是對的。這是一種典型的邏輯錯誤

✖ 套用一下邏輯學:
我是對的 則 你無法証明我是錯的,恒為真。
你無法証明我是錯的 則 我是錯的,恒為真。
你無法證明我是錯的 則 我是對的,不恒為真

案例局限

最為常見的邏輯錯誤就是“以偏概全”——某種經驗在某個人身上應驗了,並不意味著該經驗在所有人身上都會起作用。

✖ 常見的推銷話術,我作得到,你也作得到。努力就會成功。
這類的話術可怕的地方在於它不完全是錯的,但是你必需客觀衡量各種條件,而非局限單一案例。

對立論證

人們可能基於一模一樣的原因做出截然相反的決定。換句話說,用同樣的論據證明截然相反的論點。

抱怨上司“愚蠢”的人和能夠發覺“上司的愚蠢可能有另外的解釋”的人,得到的結論和採取的行動往往截然相反。因此,時間在他們接下來的經歷中所產生的伴隨作用也截然相反。這裡,時間再一次選擇與心智強大的人做朋友

一旦意識到對立論證的存在,應該“哪一個更合理、更現實就接受哪一個”,而非“哪一個更積極就接受哪一個”,因為後者只是自我欺騙而已。積極的並不總是好的,哪一個極端理想主義者(以及他們的想法、理念)不是積極的呢?

✖ 職場上不論是留下繼續努力,或是掛冠求去都無對錯之分,而是何者更為合理。這個道理在人生的選擇上也是,需要注意對立論證的存在,堅持或放棄本無對錯,合理的判斷即可。

張冠李戴

論點和論據之間儘管全無邏輯聯繫,卻可以用一種“顯然合理”的姿態綁在一起。

舉例:

將字母 A 到 Z 分別編上 1 到 26 的分數(A=1,B=2,…,Z=26),然後比較不同單詞的分值:
▷Knowledge(知識)得到 96 分
(11+14+15+23+12+5+4+7+5=96);
▷HardWork(努力)也只得到 98 分
(8+1+18+4+23+15+18+11=98);
▷Attitude(態度)才能左右你生命的全部,
因為它能得到 100 分
(1+20+20+9+20+21+4+5=100)——滿分。
得出結論:“態度改變一切”。
Bullshit(胡說八道)
(2+21+12+12+19+8+9+20=103)

感悟與道理

“道理”應該是普適的,而“感悟”只來自個體經驗。

並非只有專家級別的新聞工作者才要分清“看法與事實之間的區別”,其實每個人都需要清楚地認識“道理”和“感悟”之間的巨大差異。

如果某個人把他們說的當作全部,把他們沒說的當作沒有,那麼這個人的智商就跟寓言中那個掩耳盜鈴的傢伙屬於同一個水平了。

人類普遍擁有的一個認知偏差就是:把成功攬到自己身上,把失敗歸咎於別人或者壞運氣。(這在心理學上有個術語,叫做“自利性偏差(Self-servingBias)”。)從這裡我們就可以知道,那些“成功者”這麼做的時候,往往並不是有意欺騙——他們甚至出於好意。

一個很實用的建議:與其關注成功者,不妨反其道而行之——努力從失敗者身上汲取經驗。

✖ 困難的點在於,感悟難以作”雙盲測試”,也深受”案例局限”與”安慰劑效應”影響。

克服恐懼

“道理都明白,可就做不到”

輔助工具

語言

以下一些句式最好經常使用,因為它們特別有助於獨立思考習慣的養成,並且也有刺激思考的作用:
▷……是一回事,而……是另外一回事。
▷……和……其實根本不是一回事。
▷……不一定……
▷……。可是,這並不意味著……
▷……也許還有另外一種可能性(解釋)。
▷……看起來像……,可是……
▷……。而事實卻可能遠比看起來的更為複雜(簡單)。
▷……。然而,(這個論斷)反過來(陳述)卻不一定成立……
▷……其實很可能與……根本就沒有任何關係。
▷……和……之間不一定是單純的因果關係,它們也可能互為因果。
▷……和……之間的比較也許沒有任何意義。
▷……其實不過是表面現象,其背後的本質是……
▷……有一個通常被忽略的前提。
▷……儘管聽起來很有道理,然而卻完全不現實。
▷……也許有人會說……,但是這種質疑卻……

要刻意迴避的句子
▷……難道就沒有一點可取之處嗎?
▷……要是……就好了!

六、交流

學會傾聽

為了真正做到有效傾聽,最需要克制的就是“過早質疑”。

就算需要質疑,也一定要等到對方把話說完。

說與不說

“知無不言,言無不盡”在大部分情況下是最浪費時間和精力的做法。

…在分辨談話對象之前很可能要先分辨自己,所以,
“可與言而不與言,失人。不可與言而與之言,失言。”這是說要分辨談話的對象。

共生狀態

見瓦茨拉夫·哈維爾先生製定的《對話守則》: 1.對話的目的是尋求真理,不是為了鬥爭。 2.不做人身攻擊。 3.保持主題。 4.辯論時要用證據。 5.不要堅持錯誤不改。 6.要分清對話與只准自己講話的區別。 7.對話要有記錄。 8.盡量理解對方。

雙方想要進行有意義的討論,最基本的要求就是雙方必須共同遵守“理性討論基本原則”。

跨越“自以為是”

✖ 「緣督以為經;因其故然」— 莊子。養生主 ; 學習應順其自然,不然會把自已累死。「知知為知知,不知為不知,是知也」—論語。

第一個原則:有意義的討論是競合
第二個原則:真理是獨立存在的
第三個原則:真理不變

正確複述

為了保證溝通順利,往往需要添加一個驗證機制,或者說反饋機制。

勤於反思

  • 深刻了解經驗的局限
  • 時時刻刻保持警惕

手裡只有一把錘子,看什麼問題都像釘子

  • 用記錄,使自己能夠記住更多的經驗
  • 通過觀察和閱讀汲取他人的經驗
  • 經常試用類比來跨越未知與已知的障礙
  • 耐心等待以獲得不能跨越時間的經驗

七、應用

興趣

說來說去,是順序出了問題:往往並不是有興趣才能做好,而是做好了才有興趣。人們總是搞錯順序,並對這樣的錯誤毫不知曉。雖然並非絕對,但完成大多數事情,確實都需要熟能生巧。做得多了,自然就能擅長;擅長了,自然就做得比別人好;做得比別人好了,興趣就濃起來了,而後就更喜歡做、更擅長……進入良性循環。可同樣的,做得多,就需要大量的時間投入,所謂“沒興趣”,往往不過是結果,如果將它當作“不去做好”的理由,最終的懲罰就是大量時間白白流逝。

方法

所有學習上的成功,都只依靠兩件事——策略和堅持,而堅持本身就是最重要的策略。堅持,其實就是重複;而重複,說到底就是時間的投入,準確地說,是大量時間的投入。

✖ 你沒有實際作之前,不會知道效果如何,再在找更好的方法之前,最好的策略是堅持。

痛苦 -比較 - 運氣

人脈

當我們把時間花到一個人身上的時候,相當於在他身上傾注了自己生命的一段。

▷ 專心做可以提升自己的事情,學習並擁有更多、更好的技能,成為一個值得他人交往的人。
▷ 學會獨善其身,以不給他人製造麻煩為美德,用自己的獨立贏得尊重。

✖ 人必自重而後人重之—你值得人家交往,人家就會來跟你交往。

自卑

✖ 保持謙卑,認識真正的自已。

靈感

✖ 靈感來自於累積,筆記是一個好工具。

鼓勵

效率

節奏

物極必反

  • 絕對不要盲目地試圖減少睡眠時間
  • 盡量不要減少與家庭成員交流的時間
  • 最好不要放棄你的社交時間

自我證明

參考

(fin)

.NET PDB File 介紹

PDB(Program Database Files )

.Net 原始碼在 Debug mode 建置後,將產生.pdb 檔案,其中記錄了.dll、.exe 與原始碼之間的對應關係。

MSDN 這樣說

程式資料庫 (.pdb) 檔案也稱為符號檔,可將您在原始程式檔中為類別、
方法和其他程式碼建立的識別項對應至專案之編譯的可執行檔中使用的識別項。

原文:

A program database (.pdb) file, also called a symbol file,
maps the identifiers that you create in source files for classes,
methods, and other code to the identifiers that are used in the
compiled executables of your project.

PDB 記什麼

  • 原始碼的檔案名稱 (source code file name)
  • 變數與行號 (lines and the local variable names.)

偵錯工具搜尋 .pdb 檔案的順序?

  1. DLL 或可執行檔內部指定的位置
  2. 可能和 DLL 或可執行檔存在相同資料夾中的 .pdb 檔案。
  3. 任何本機符號快取資料夾。
  4. 任何在像是 Microsoft 符號伺服器 (如果啟用) 等位置指定的網路、網際網路或本機符號伺服器和位置。

.pdb 檔案與.dll(或可執行檔)需要完全符合

偵錯工具只會載入與可執行檔建置時所建立的 .pdb 檔案完全相同之可執行檔的 .pdb 檔案
*相同原始碼在不同機器建置的執行檔與 PDB,偵錯工具將無法進行偵錯

看看.pdb 檔的內容

  1. 開啟 %ProgramFiles(x86)%\Microsoft Visual Studio 14.0\DIA SDK\Samples\DIA2Dump\dia2dump.sln

  2. 執行以下命令

1
2
> Dia2Dump pdbfilepath >> dumpfileName.txt
> Dia2Dump C:\myproj\bin\debug\myproj.pdb >> myproj_dump.txt`
  1. 輸出的結果大致如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function       : In MetaData, [00001F16][0001:00001F16], len = 000000ED, GetBookList
Function attribute:
Function info:


//中略

Function: [0001F71A][0001:0001F71A] GetSalePage b_231
Function: [00001F16][0001:00001F16] GetBookList
Function: [0001F4F0][0001:0001F4F0] GetLayoutByAd b_0

//中略

** GetBookList

line 46 at [00001F16][0001:00001F16], len = 0x1 D:\Repo\Mark_Lin\PROJ\WebAPI\Controllers\BookController.cs
line 47 at [00001F17][0001:00001F17], len = 0x6
line 48 at [00001F1D][0001:00001F1D], len = 0x1C

參考

(fin)

ASP.NET Thread Pool 與 Redis Timeout Exception

概述

ASP.NET Thread Pool 的機制如何影響 Redis

案例

線上維護的系統偶爾會發生 Redis Timeout Exception ,
並在 elmah 發現以下的錯誤記錄

1
2
3
Timeout performing SETEX Cache:Prod:WebAPI:Key:20161121152607, inst: 18, mgr: ExecuteSelect, err: never,
queue: 0, qu: 0, qs: 0, qc: 0, wr: 0, wq: 0, in: 0, ar: 1,
IOCP: (Busy=0,Free=1000,Min=8,Max=1000), WORKER: (Busy=13,Free=32754,Min=8,Max=32767), clientName: TYO-MWEB

解析

這時當 StackExchange.Redis 在進行同步作業的時候,
如果超過 syncTimeout 的設定值(預設是 1000ms),
Redis 會佔用.NET 的 worker Thread
而在 .NET 底層隱含著一個機制,
會導致錯誤。

Thread Pool 500ms 的機制

一種簡化的說法「 ASP.NET Thread Pool 一秒能建立 2 個 Thread。」

設定值 minworkerthread 就像是遊樂場已經開啟的閘門,
每當有一個遊客(Task)進來時,立即提供給它使用。
但是當遊客(Task)變多的時候,就會開始排隊(Queue),
ASP.NET Thread Pool 隱含著一個機制,
當它的隊伍(Queue)長達 500 豪秒沒有移動的話,
就會開啟新的閘門(建立新的 Thread)。
而我的情境屬於Burst of traffic,
突然大量 Task 湧入 Queue ,
ThreadPool 需要大量的 Thread
每個新的 Thread 都需要 500ms 的反應時間 ,
而累積的時間超過 Task 的 Timeout 設定值時 ,
就會拋出 Exception.

ASP.NET Thread Pool

ASP.NET Thread Pool 的排隊機制與minworkerthread 設定值相關。
可以透過調整 machine.config 來修正。
參考(爭用、 效能不佳、 和死結 (deadlock) 當您從 ASP.NET 應用程式呼叫 Web 服務)。

minworkerthread 的預設值是 1 。
但是會與執行環境的 CPU 個數有關,
假設你是四核的主機,那就要乘上 4。

1
2
3
<system.web>
<processModel autoConfig="false" minWorkerThreads="1" />
</system.web>

範例

當 Redis 發生 Timeout 時,
可以透過錯誤訊息判斷其背後的原因是否與 workerthread 有關。
在以下的例子可以看到 IOCP 與 WORKER 兩個值。
這兩值表示 .Net ThreadPool 內的兩種執行緒,
如果 Busy 值很高, 就有可能是 ThreadPool 來不及建立造成的錯誤。

1
2
3
System.TimeoutException: Timeout performing MGET 2728cc84-58ae-406b-8ec8-3f962419f641,
inst: 1,mgr: Inactive, queue: 73, qu=6, qs=67, qc=0, wr=1/1, in=0/0
IOCP: (Busy=0, Free=999, Min=2,Max=1000), WORKER (Busy=7,Free=8184,Min=2,Max=8191)

Error Code 說明

Error code Details 範例 說明
inst in the last time slice: 0 commands have been issued 在最後時脈:已發出 0 個命令 最後的時脈發出的命令個數
mgr the socket manager is performing “socket.select”,
which means it is asking the OS to indicate a socket
that has something to do;
basically: the reader is not actively reading from
the network because it doesn’t think there
is anything to do
最後的操作命令
queue there are 73 total in-progress operations 73 個正在排隊中的操作 正在排隊中的操作
qu 6 of those are in unsent queue: they have not yet been written to the outbound network 6 個未發送的 queue 未發送的 queue
qs 67 of those have been sent to the server but a response is not yet available. The response could be: Not yet sent by the server sent by the server but not yet processed by the client. 67 個已發送的 queue 已發送的 queue
qc 0 of those have seen replies but have not yet been marked as complete due to waiting on the completion loop 0 個已發送未標記完成的 queue 已發送未標記完成的 queue
wr there is an active writer (meaning - those 6 unsent are not being ignored) bytes/activewriters 有 1 個啟用的 writer,(意味著 qu 的工作並沒有被忽略) bytes/activewriters bytes/activewriters
in there are no active readers and zero bytes are available to be read on the NIC bytes/activereaders 0 個 reader bytes/activereaders

參考


其它的 Redis Error 情境

只作摘要式的翻譯。

Memory pressure (記憶體壓力)

Problem:
記憶體不足而開始讀取虛擬記憶體(磁碟)而導致特能低落。
Memory pressure on the client machine leads to all kinds of performance problems that can
delay processing of data that was sent by the Redis instance without any delay.
When memory pressure hits, the system typically has to page data from physical memory to
virtual memory which is on disk. This page faulting causes the system to slow down significantly.

Measurement:
Monitory memory usage on machine to make sure that it does not exceed available memory.
Monitor the Page Faults/Sec perf counter. Most systems will have some page faults even during
normal operation, so watch for spikes in this page faults perf counter which correspond with timeouts.

Resolution:

增加記憶體或是減少記憶體使用量
Upgrade to a larger client VM size with more memory or dig into your memory usage patterns
to reduce memory consuption.

Burst of traffic

Problem:

ThreadPool 突然大量的工作湧入 queue 導致執行緒來不及建立。
Bursts of traffic combined with poor ThreadPool settings can result in delays in processing
data already sent by the Redis Server but not yet consumed on the client side.

測量:

可以用程式監控 ThreadPool .
或是單純透過 TimeoutException 的訊息,觀察 IOCP 與 WORKER 的 Busy 值來判斷.

Measurement:

Monitor how your ThreadPool statistics change over time using code like this.
You can also look at the TimeoutException message from StackExchange.Redis.
Here is an example :

1
2
System.TimeoutException: Timeout performing EVAL, inst: 8, mgr: Inactive, queue: 0, qu: 0, qs: 0, qc: 0, wr: 0, wq: 0, in: 64221, ar: 0,
IOCP: (Busy=6,Free=999,Min=2,Max=1000), WORKER: (Busy=7,Free=8184,Min=2,Max=8191)

In the above message, there are several issues that are interesting:
Notice that in the “IOCP” section and the “WORKER” section you have a “Busy” value that is
greater than the “Min” value. This means that your threadpool settings need adjusting.
You can also see “in: 64221”. This indicates that 64211 bytes have been received at the kernel
socket layer but haven’t yet been read by the application (e.g. StackExchange.Redis).
This typically means that your application isn’t reading data from the network as quickly as
the server is sending it to you.

Resolution:

調整 ThreadPool 設定
Configure your ThreadPool Settings to make sure that your threadpool will scale up quickly under burst scenarios.


High CPU usage (CPU 過載)

Problem:

High CPU usage on the client is an indication that the system cannot keep up with the work that it has been asked to perform. High CPU is a problem because the CPU is busy and it can’t keep up with the work the application is asking it to do. The response from Redis can come very quickly, but because the CPU isn’t keeping up with the workload, the response sits in the socket’s kernel buffer waiting to be processed. If the delay is long enough, a timeout occurs in spite of the requested data having already arrived from the server.

Measurement:

Monitor the System Wide CPU usage through the azure portal or through the associated perf counter.
Be careful not to monitor process CPU because a single process can have low CPU usage at the same time that overall system CPU can be high.
Watch for spikes in CPU usage that correspond with timeouts. As a result of high CPU,
you may also see high “in: XXX” values in TimeoutException error messages as described above in the “Burst of traffic” section.
Note that in newer builds of StackExchange.Redis, the client-side CPU will be printed out in the timeout error message as long as the environment doesn’t block access to the CPU perf counter.

Note:

StackExchange.Redis version 1.1.603 or later now prints out “local-cpu” usage when a timeout occurs to help understand when client-side CPU usage may be affecting performance.

Resolution:

增加 CPU 或是找出 CPU 產生過載的原因
Upgrade to a larger VM size with more CPU capacity or investigate what is causing CPU spikes.


Client Side Bandwidth Exceeded (頻寬不足)

Problem:

Different sized client machines have limitations on how much network bandwidth they have available.
If the client exceeds the available bandwidth, then data will not be processed on the client side as quickly as the server is sending it. This can lead to timeouts.

Measurement:

Monitor how your Bandwidth usage change over time using code like this. Note that this code may not run successfully in some environments with restricted permissions (like Azure WebSites).

Resolution:

加大頻寬或減少使用量
Increase Client VM size or reduce network bandwidth consumption.

Large Request/Response Size (過大的請求/回應量)

Problem:

如圖所示,A 與 B 兩個 Request 都太過龐大,當同時發動請求時,
A 回應的時間過長, 導致 B 的 Timeout
A large request/response can cause timeouts. As an example, suppose your timeout value configured is 1 second.
Your application requests two keys (e.g. ‘A’ and ‘B’) at the same time using the same physical network connection.
Most clients support “Pipelining” of requests, such that both requests ‘A’ and ‘B’ are sent on the wire to the server one after the other without waiting for the responses.
The server will send the responses back in the same order. If response ‘A’ is large enough it can eat up most of the timeout for subsequent requests.

Below, I will try to demonstrate this. In this scenario, Request ‘A’ and ‘B’ are sent quickly,
the server starts sending responses ‘A’ and ‘B’ quickly, but because of data transfer times,
‘B’ get stuck behind the other request and times out even though the server responded quickly.

1
2
3
4
5
6
|-------- 1 Second Timeout (A)----------|
|-Request A-|
|-------- 1 Second Timeout (B) ----------|
|-Request B-|
|- Read Response A --------|
|- Read Response B-| (**TIMEOUT**)

Measurement:

This is a difficult one to measure. You basically have to instrument your client code to track large requests and responses.

Resolution:

將所需要的資料分割成數個小片段 再分別取回
Redis is optimized for a large number of small values, rather than a few large values.
The preferred solution is to break up your data into related smaller values. See here for details around why smaller values are recommended.
Increase the size of your VM (for client and Redis Cache Server), to get higher bandwidth capabilities,
reducing data transfer times for larger responses.
Note that getting more bandwidth on just the server or just on the client may not be enough.
Measure your bandwidth usage and compare it to the capabilities of the size of VM you currently have.
Increase the number of ConnectionMultiplexer objects you use and round-robin requests over different connections (e.g. use a connection pool).
If you go this route,make sure that you don’t create a brand new ConnectionMultiplexer for each request as the overhead of creating the new connection will kill your performance.

(fin)

有關 HTTP Header Content-Type

引言

當我們對 Server 發出 request 的時候
需要註明你 request 的 Content-Type
以下簡單介紹一下這些格式

測試工具

PostMan這套工具
可以模擬不同的格式資料發動 request 到你的 Server


Content-Type

application/x-www-form-urlencoded

常用的 Content-Type,簡單說就是 KEY-VALUE 的方式
如下, KEY firstname 的值是 marsen
lastname 是由使用者輸入

1
2
3
4
5
6
<form>
First name:<br />
<input type="text" name="firstname" value="marsen" /><br />
Last name:<br />
<input type="text" name="lastname" />
</form>

同時資料會作一次 url encoded,
產生類似下列的資料
firstname=marsen&lastname=lin&key%5b1%5d=value%5b1%5d


multipart/form-data

PostMan 中的選項 binary 其實就是包成這種格式
上傳檔案會使用這種 Content-Type,
這通常表示你的 html element 包含有 <input type="file">


其它

PostMan 中的選項 raw,可以用字串組合成任意 Content-Type,
參考Content-Type Table

  • application/json
    目前主流的 API 會用輕巧的 JSON 作為傳遞資訊的媒介
  • text/xmlapplication/xml
    早期標準的 Web 服務通常會透過 xml 作為交換資訊的媒介
  • text/plain
    有些 email 或 debug 的情況會使用 text/plain 作為 Content-Type,但是一般的 Request 情況不建議使用
  • 更多請參考Spec

參考

(fin)

[記錄]PowerShell 初體驗

需求

  1. 將指定的 Log 記錄,匯入資料庫,產生 row data
  2. 將 row data 轉換成為需要的報表資料
  3. 產生報表

規劃

  1. powershell 讀取檔案
  2. powershell 連接資料庫
  3. powershell 執行 SQL
  4. powershell 作 BulkInsert
  5. powershell 寫入檔案

PowerShell

簡記要點

  • powershell 可以直接取用 .Net Framework 或 COM 元件
  • 宣告變用要用$字號
  • # 是註解

讀取檔案

1
2
3
4
5
6
#用New-Object 建立.Net StreamReader 物件
$reader = New-Object System.io.streamReader(get-item $filePath)
#使用`[]`建立靜態類別讀取檔案
$file = [System.IO.File]::ReadAllLines($filePath)
#直接使用Get-Content讀取文檔
$file = Get-Content "C:\filepath\file"

連線資料庫與執行語法

1
2
3
4
5
$connection = New-Object System.Data.SQLClient.SQLConnection
$connection.ConnectionString = "server='$server';database='$database';uid='$user'; pwd='$pwd';Integrated Security=False;"
$connection.Open()
# do something
$connection.Close()

BulkInsert

  • 從檔案建立 DataTable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$table = New-Object System.Data.DataTable
#建立欄位
$col_title = New-Object system.Data.DataColumn "Title",([string])
$table.Columns.Add($col_title);
$col_content = New-Object system.Data.DataColumn "Content",([string])
$table.Columns.Add($col_content);
$col_author = New-Object system.Data.DataColumn "Author",([string])
$table.Columns.Add($col_author);
#建立資料
foreach($file in $files){
$dr = $table.NewRow();
$dr["Title"] = $file["title"]
$dr["Content"] = $file["content"]
$dr["Author"] = $file["author"]
}
#寫入資料表
$table.Rows.Add($dr);
  • 透過 BulkCopy 將 DataTable 寫入資料庫
1
2
3
4
5
$connection.Open()
$bulkCopy = New-Object (“Data.SqlClient.SqlBulkCopy”) -ArgumentList $connection
$bulkCopy.DestinationTableName = "tablename"
$bulkCopy.WriteToServer($datatable)
$connection.Close()

進度條

1
Write-Progress -Activity "BulkInsert" -Status "載入百分比: 100 %" -PercentComplete 100;

產生報表

1
$datatable | export-csv C:\Reports\20161026.csv -Encoding UTF8

參考

  1. https://msdn.microsoft.com/en-us/powershell
  2. https://msdn.microsoft.com/en-us/powershell/scripting/getting-started/cookbooks/using-static-classes-and-methods
  3. https://cmatskas.com/execute-sql-query-with-powershell/
  4. https://blogs.technet.microsoft.com/heyscriptingguy/

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (二) - 用JQuery實作

設計理念

  1. 顯示/新增/刪除 TodoList
  2. TodoList 會是一堆 todoItem 的集合,所以要定義 todoItem 的形別
    • Content : todoItem 的內容
    • Status : todoItem 的狀態,完成(done)、未完成(undo) ,設計成列舉
  3. 主要的功能
    • 建立 todoItem
    • 完成 todoItem
    • 繪製 todoList 到前端的畫面上

自我分析

跟 UI 耦合太高,Render 應該與 TodoService 分離 ,
DOM 註冊事件相依在 Service 裡面要抽離也不好抽 。
沒有先寫測試 , 要想一想怎麼與 UI 層作隔離。

程式碼

建立 BaseService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BaseService<T> {
constructor(private type: string) {}

List: Array<T>;

Create(data: T) {
this.List.push(data);
}

Delete(data: T) {
var index = this.List.indexOf(data);
this.List.splice(index, 1);
}

Render() {}
}

建立 todoItem interface

1
2
3
4
interface todoItem {
Content: string;
Status: todoStatus;
}

建立 todoItem Status 列舉

1
2
3
4
enum todoStatus {
undo,
done,
}

建立 TodoService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
class TodoService extends BaseService<todoItem> {
constructor() {
super("todoItem");
}

public Render(): void {
let doneHtml = "";
let undoHtml = "";

this.List.forEach((item) => {
if (item.Status == todoStatus.done) {
doneHtml += `<li>${item.Content}<button class="recover-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-share-alt"></span></button><button class="remove-item btn btn-default btn-xs pull-right"><span class="glyphicon glyphicon-remove"></span></button></li>`;
} else if (item.Status == todoStatus.undo) {
undoHtml += `<li class="ui-state-default"><div class="checkbox"><label><input type="checkbox" value="" />${item.Content}</label></div></li>`;
}
});
$("#done-items").html(doneHtml);
$("#sortable").html(undoHtml);
$(".add-todo").val("");
}

Delete(data: todoItem) {
data.Status = todoStatus.done;
}

Init() {
//// todoList in localStorage
var list = window.localStorage.getItem("todoList");
if (!list) {
this.List = new Array<todoItem>();
} else {
this.List = JSON.parse(list);
}
window.onbeforeunload = (evt) => {
window.localStorage.setItem("todoList", JSON.stringify(this.List));
};

// mark task as done
$(".todolist").on(
"change",
'#sortable li input[type="checkbox"]',
(evt) => {
var self = evt.target;
var text = $(self).parent().text();
if ($(self).prop("checked")) {
var doneItem = this.List.filter((i) => {
return text == i.Content;
})[0];
this.Delete(doneItem);
this.Render();
}
}
);

$(".add-todo").on("keypress", (evt) => {
evt.preventDefault;
if (evt.which == 13) {
if ($(evt.target).val() != "") {
var todo = $(evt.target).val();
this.Create({
Content: $(evt.target).val(),
Status: todoStatus.undo,
});
this.Render();
} else {
// some validation
}
}
});

$(".todolist").on("click", "#done-items li button.recover-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var recoverItem = this.List.filter((i) => {
return text == i.Content;
})[0];
recoverItem.Status = todoStatus.undo;
this.Render();
});

$(".todolist").on("click", "#done-items li button.remove-item", (evt) => {
var text = $(evt.target).parent().parent().text();
var removeItem = this.List.filter((i) => {
return text == i.Content;
})[0];
var index = this.List.indexOf(removeItem);
this.List.splice(index, 1);
this.Render();
});

$("#checkAll").on("click", (evt) => {
this.List.forEach((item) => {
item.Status = todoStatus.done;
});
this.Render();
});

//// Render
this.Render();
}
}

使用建立好的 TodoService

1
2
var todoService = new TodoService();
todoService.Init();

(fin)

[KATA] 用 TypeScript 作一個簡易的 TodoList (一) - 前置作業

目標

  1. 使用 typescript 開發
  2. 顯示/新增/刪除 TodoList

功能分析

  1. 只是練習,故不開發 server side 的程式
  2. 暫時存在 cookie 上
  3. 用 bootstrap 作簡單的樣式

實作記錄

UI : 使用 Bootstrap 沒有必要重新打造輪子,能用的就拿來用

  • 找到一個 TodoList 的樣版,內含 HTML 、 CSS 與 JS ,功能完整.
  • 取用樣版的 HTML.
  • 引用 bootstrap 3.3.5 CDN 上的 css .
  • 建立一個 todo.css 直接引用樣版的 css 並加入頁面參考.

開發環境

  1. 安裝 typescript
    npm install typescript --save
  2. 安裝 gulp
    npm install gulp
    npm install --global gulp
  3. 安裝 gulp-typescript
    npm install gulp-typescript
  4. 建立 gulpfile.js
1
2
3
4
5
6
7
8
var gulp = require("gulp");
var tsc = require("gulp-typescript");
gulp.task("default", function () {
return gulp
.src("public/javascripts/**/*.ts")
.pipe(tsc())
.pipe(gulp.dest("public/javascripts/"));
});

Typescript

  1. 關於 typescript 的定義檔, 以前有 tsd 與 typings 兩種管理工具,現在可以更簡便的合併到 npm 作管理 .

  2. 透過 TypeSearch 可以找到 bootstrap 的 typescript 定義檔.

  3. 執行 npm install --save @types/bootstrap 安裝 bootstrap (目前的版本是 Bootstrap 3.3.5) , 因為相依於 jquery 所以也會一併安裝

  4. 安裝 jquery-ui 的定義檔
    npm install --save @types/jqueryui

  5. 新增檔案 todo.ts ,將 樣版 的 javascript 複製貼上 .
    *註:因為 typescript 是 javascript 的 superset , 完全可以相容原生 javascript, 如果有任何錯誤, TypeScript 將會提示你

  6. todo.ts 引用 jquery 、jquery-ui 與 bootstrap 的 typescript 定義檔.

    1
    2
    3
    /// <reference path="../../../node_modules/@types/jquery/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/bootstrap/index.d.ts" /> ///
    <reference path="../../../node_modules/@types/jqueryui/index.d.ts" />
  7. 頁面載入對應的 js 檔,記得放置順序 jquery 要在最前面,並將放在<\body>之後

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <script
    src="http://code.jquery.com/jquery-2.2.4.min.js"
    integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44="
    crossorigin="anonymous"
    ></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <script
    src="http://code.jquery.com/ui/1.12.1/jquery-ui.min.js"
    integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU="
    crossorigin="anonymous"
    ></script>
  8. 執行 gulp ,會產生 todo.js

  9. 頁面載入 js 檔,記得要放在相依的 js( jquery 、 bootstrap 、 jqueryui)之後.

1
<script type="text/javascript" src="/javascripts/kata/todo.js"></script>

截至目前為止,僅僅是在作複製樣版,同時一併處理一些基本 gulp 與 typescript 相關的配置
接下來才要開始寫 typescript

(待續)

參考

  1. 關於 TypeScript 2.0 之後的模組定義檔 ( Declaration Files ) ( *.d.ts )
  2. bootsnipp
  3. TYPESCRIPT + EXPRESS + NODE.JS

(fin)

Please enable JavaScript to view the LikeCoin. :P