TypeScript 學習筆記(3) – 基本型別

今天是看這篇:Basic Types

裏面直接建議用 ES6 的 let 替代 var 了。

  • boolean:布林型態
  • number:數字型態,沒有 integer, long, float, double 之類的,只有 number,JavaScript 裡的數字都是 float。
  • string:字串
  • array:陣列,在型別後面加上 [],例如: let list: number[] = [1, 2, 3]; ,要注意的是,無法指定固定個數。
  • tuple:就 tuple ,有點像陣列,但能指定每個元素的型態,這跟 Python 不一樣。例如:
    let x: [string, number];
    let x = ["Hello", 100];
    let y: [string, number] = ["John", 200];
    y[2] = "Doe";
    console.log(y);  // 輸出 [ 'John', 200, 'Doe' ]
    
  • enum:列舉,用法跟其他語言大致相似:
    enum Color {Red, Green, Blue};
    let c: Color = Color.Green;
    // 比較特別的用法
    let colorName: string = Color[2];
    console.log(colorName);  // 輸出 Blue
    
  • any:任意型態,但要注意的是跟其他語言所指的 Object 型別不同,TypeScript 有獨立一個 Object 型別。說起來跟 C# 的 dynamic 比較相似。
    let notSure: any = 4;
    notSure.ifItExists(); // 通過編譯,因為可能在執行時期就有 ifItExists()
    notSure.toFixed(); // 通過編譯,因為可能在執行時期就有 toFixed()
    
    let prettySure: Object = 4;
    prettySure.toFixed(); // 編譯錯誤,編譯時會有 Object 型別沒有 toFixed 的錯誤。
  • void:就 void ,表示不會傳回任何東西
  • null:就 null ,可以把任何型態的變數指定為 null
  • undefined:就 undefined,可以把任何型別的變數指定為 undefined
  • never:表示絕對不會傳回變數,
    // 只會丟出例外
    function error(message: string): never {
        throw new Error(message);
    }
    // 裏面是不會結束的迴圈
    function infiniteLoop(): never {
        while (true) {
        }
    }

Type assertion ,有 C/C# 強制轉型的意味。

let someValue: any = "this is a string";
let strLength: number = (someValue).length;
// 也可以用類似 c# as 的語法
let strLength: number2 = (someValue as string).length;

 

TypeScript 學習筆記(2) – Type annotation/Interface/Class

以下是看 Quick start 的紀錄:

  • Type annotation:一般動態語言是沒型別的,但 TypeScript 加上了這部份的支援,這可以讓開發者在編譯時期就預先發現型別錯誤,也可以編譯出更有效率的程式,讓程式執行的更快。語法像是 Go 或 Pascal (知道這語言的人應該不多了),是在變數後面加上 :type,例如:
    var str:string="hello world";
  • Interface:介面,沒啥特別的,看範例比較快
    interface Person {
        firstName: string;
        lastName: string;
    }
    
    function greeter(person: Person) {
        return "Hello, " + person.firstName + " " + person.lastName;
    }
    
    var user = { firstName: "Jane", lastName: "User" };
    
  • Class:大致上跟 java 的用法相似,不過在看到 QuickStart 範例時,還是驚訝了一下,主要是因為 TypeScript 語法並沒有明確指定類別實作了 Person 介面,但這個類別所產生的物件仍可以直接丟到只接介面的函式裡。後來仔細看了一下,才知道是因為 TypeScript 的 constructor (建構子)裡的參數加了 public,那麼會自動將這個參數放到同名的屬性去,也因此就符合了 Person 介面的要求。
    class Student {
        fullName: string;
        // 這裡的 public firstName,等同是函式裡有 this.firstName=firstName
        constructor(public firstName, public middleInitial, public lastName) {
            this.fullName = firstName + " " + middleInitial + " " + lastName;
        }
    }
    
    // 故意不要有 lastName
    class Employee {
        constructor(public firstName) {
        }
    }
    
    interface Person {
        firstName: string;
        lastName: string;
    }
    
    // 因為 Student 裡有 firstName, lastName 屬性,視同實作了 Person
    function greeter(person : Person) {
        return "Hello, " + person.firstName + " " + person.lastName;
    }
    
    var user = new Student("Jane", "M.", "User");
    var employee = new Employee("John");
    console.log(greeter(user));
    // console.log(greeter(employee));  // 加了這行,編譯會發生錯誤,告知 employee 不符合 Person 介面
    

TypeScript 學習筆記(1) – 安裝與執行

我習慣用的環境是 Ubuntu,目前用的是 16.04 Xenial。

第一步是安裝,安裝 nodejs 有幾種方法:

  1. nodejs.org 下載 tarball,手動安裝。
  2. 用 debian package 來安裝 (nodesource)
  3. 用 nvm 來安裝

之前有用過 nvm 了,這次我選擇用 debian package 來安裝 LTS 版的 nodejs。

curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
sudo apt-get install -y nodejs

安裝好,就可以利用 npm 來安裝 TypeScript 了。不過,因為我不想把 typescript 安裝在系統路徑 (一般是用 sudo npm install -g typescript),所以我多設定了 .npmrc 以及環境變數

prefix = /home/user/.local
root = /home/user/.local/lib/node_modules
binroot = /home/user/.local/bin
manroot = /home/user/.local/share/man

 

export LOCAL_PATH="$HOME/.local"
export MANPATH="$LOCAL_PATH/share/man:$MANPATH"
export NODE_PATH="$LOCAL_PATH/lib/node_modules:$NODE_PATH"
export PATH="$LOCAL_PATH/bin:$PATH"

 

在設定好以後,使用 npm install -g 時,會將這些套件安裝到自己的 $HOME/.local 目錄下。

在使用 npm install -g typescript 以後,我另外安裝了去年推出的 yarn。

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt install yarn

 

首先寫一個 hello.ts

console.log("Hello world.");

然後用 tsc 來將 hello.ts 編譯為 hello.js: tsc hello.ts ,最後就可以用 node 執行 hello.js 印出 “Hello world.”

groovy 的編譯

用 Gradle 做很方便,build.gradle 裡要加

apply plugin: 'groovy'

然後程式碼依照 The Groovy Plugin – Gradle User Guide Version 3.2.1 裡的說明來安排就可以:

目錄 放什麼
src/main/java Java 程式碼 (對,可以跟 Java 一起編譯)
src/main/resources 會用到的資源
src/main/groovy Groovy 程式碼,也可以放 Java 程式碼
src/test/java Java 測試案例
src/test/resources 測試案例會用到的資源
src/test/groovy Groovy 測試案例
src/sourceSet/java Java source for the given source set
src/sourceSet/resources Resources for the given source set
src/sourceSet/groovy Groovy sources for the given source set. May also contain Java sources for joint compilation.

然後執行 gradle build 進行編譯,接著就可以在 build 裡找到 jar 檔案。

但是要注意的是,這個 jar 檔案不是 executable jar 檔案 (用 java -jar 就可執行的 jar ),要編譯為 executable jar 檔,得多做一些工。

第一個是要在 build.gradle 裡指定 manifest ,也就是在製作 jar 時,指定 META-INF ,告訴 java -jar 說,該執行哪個類別裡的 main。像下面就是自訂 Jar task ,並指定 manifest 的程式。

task uberjar(type: Jar) {
    from files(sourceSets.main.output.classesDir)
    from configurations.runtime.asFileTree.files.collect { zipTree(it) }

    manifest {
        attributes 'Main-Class': 'Program'  // 這邊就是依照 groovy 的類別與程式來放,假設下面的 groovy 程式放在 src/main/groovy/program.groovy
    }
}

接著,在 groovy 的主程式裡,加入 main ,這個 main 其實跟 java 的規定一樣,必須是 public, static ,接受字串陣列。


class Program {
  public static void main(String[] args){
    println "Hello world"
  }
}

最後執行 gradle uberjar ,就可以得到 executable jar 了
參考資料:

用指定的 SSH key 來操作 git

一般來說,都是直接使用 $HOME/.ssh 下這把預設產生的 id_rsa/id_rsa.pub 。那如果要使用另外的 SSH key 該怎麼辦呢?

這時候可以使用 GIT_SSH 這個環境變數搭配自訂的腳本來做:

  1. 先新增一個 custom_ssh.sh:
    #!/bin/sh
    exec /usr/bin/ssh -o StrictHostKeyChecking=no -i /home/me/my_private_key "$@"
    
  2. 在使用 git 的時候,提供 GIT_SSH 環境變數
    GIT_SSH="custom_ssh.sh" git clone your_repository

    ,這樣就可以了。

在什麼情況會用到額外的 SSH key 呢? 最常見的例子就是佈署:在 gitlab/github 裡可以有所謂的 deploy key,這樣在佈署的時候,就可以不提供自己的 SSH key 來做佈署,只透過這把 deploy key 來取得原始碼,增加安全性。

誰有用到 GIT_SSH ?

  1. Jenkins 裡可以新增 SSH key ,讓你在存取 repository 時,使用這把 SSH key。實際上 Jenkins 內部也是利用 GIT_SSH 的方法在運作,但並不是全部。
  2. Ansible 的 git module 也使用了 GIT_SSH 這個技巧 (應該是)。

也可以使用 ssh-add 搭配 ssh-agent 來替代 GIT_SSH 的作法,這樣就可以不需要額外新增一個自訂的腳本了。

產生獨立 SSH Key 的方法:

ssh-keygen -C your_comment -b 4096 -m pem -f key_filename -q -P "" -N "" 

執行上面的指令後,會產生兩個檔案,一個是公鑰,一個是私鑰。在要複製到別的地方使用時,要注意 permission 必須是 0600,僅允許擁有者存取才行。

參考自:Git clone with custom SSH using GIT_SSH error – Stack Overflow

awscwxls – 將 CloudWatch 數據匯出為 xls/xlsx

有人把程式寫好了,所以拿來用就可以:Exporting AWS CloudWatch Data | Pete Zybrick

  1. 確定有設定好 $HOME/.aws 目錄下的檔案。
  2. petezybrick/awscwxls: AWS Cloudwatch to Spreadsheet 上 git clone 下來
  3. 到 run 目錄下,先複製 properties/first.properties 為一個新的檔案,例如 properties/example.properties。然後修改內容,awscwxls 會讀取這個檔案的內容當作參數,像是要拿哪些數據、取哪些 instance、起始時間、區域等等的,都是在這邊設定。時間是用當地時間,程式會自動轉換為 UTC 時間 (AWS 上的時間)。
  4. 執行 ./runcwxls (Windows 下是 runcwxls.cmd) ,runcwxls 就會把取得的數據存到 xlsx 裡去了。

terraform + digitalocean

Terraform 對 DigitalOcean provider 的說明文件:

有例子當第一個雛型來抄抄改改還是比較快,所以主要參考資料裡的 example 來改 (要先 clone 下來),下面是我邊做邊記錄下來的步驟:

  1. 產生 ssh key:ssh-keygen -f your_key_file -t rsa
  2. 進 DigitalOcean 帳號裡,新增 ssh key,把剛剛產生的 public key 填入 (就 your_key_file.pub)  ,這邊主要參考 https://www.digitalocean.com/community/tutorials/how-to-use-ssh-keys-with-digitalocean-droplets
  3. 到 DigitalOcean 帳號的 API / Tokens 下,去新增 token ,並把 token 複製起來
  4. 用 curl -X GET -H “Content-Type: application/json” -H “Authorization: Bearer <your_token>” “https://api.digitalocean.com/v2/account/keys” 取得剛剛新增的 ssh key 的 key id  ,這個步驟主要參考 https://developers.digitalocean.com/documentation/v2/#list-all-keys
  5. 編輯 main.tf ,修改上個步驟取得的 ssh key id ,並把 connection 裡的 key file 改為步驟 1 所產生的 private key file 。
  6. export DIGITALOCEAN_TOKEN=”your_token”
  7. 執行 terraform plan / apply

參考資料:

Compile your go program inside the Docker container

golang 有官方製作的 container:https://hub.docker.com/_/golang/

用 docker pull golang 拉下來以後,切換到 go 專案目錄下 (假定是 $HOME/project),執行:

docker run --rm -v "$PWD":/usr/src/myapp -e GOBIN=/usr/src/myapp -w /usr/src/myapp golang:1.6 bash -c make

就可以在專案目錄下的 bin 裡找到 binary 了。

用 docker container 來 build 的好處,除了可以指定版本之外,也可以 cross compile ,另外就是可以省下處理佈署 golang 開發環境的心思。

docker-hackmd relative url

hackmd 是一個很棒的協作平台,你可以用 markdown 來撰寫文件,graphviz/flowchart 等語法來畫圖…很厲害。

安裝上也蠻簡單的,已經有人做好 Dockerfile :hackmdio/docker-hackmd: docker hackmd image

可是這個 docker image 有個問題,就是沒辦法以 relative url 存在,他預設是在根目錄下運作,有個日本人弄出來了:HackMDをnginxで / 以外のlocationで起動する。 – Qiita ,我參考他的設定,做了調整,加入 nginx 設定與 upstart 設定,放在 elleryq/docker-hackmd: docker hackmd image

大致調整以下東西:

  1. nginx 設定:加入 rewrite,將路徑改寫為 /hackmd,這可以參考 nginx.conf.example
  2. common.js:因為 hackmd 用到 websocket ,common.js 的 urlpath 也要跟著調整,否則會無法運作,裏面的 urlpath 需要修改為 /hackmd。這部份我寫在 hackmd/Dockerfile 裡,在用 git clone 取得 hackmd 原始碼以後,用 sed 去做字串的替換。
  3. upstart:upstart.hackmd.conf 裡是用 docker-compose 啟動 hackmd image ,這邊我預期 docker-compose.yml 是放在 /srv/docker-hackmd ,如果你預期不放在這兒,那麼這邊也要跟著調整。

應該大概就這些,如果有沒提到的,就看原始碼吧~

phpvirtualbox

無意在 /etc/init.d 下看到 vboxweb-service 這個服務,好奇的把名字丟到 google 去找,才知道是 virtualbox 要提供給 phpvirtualbox 使用的服務。

安裝方法不難,不過我還是搞了好一陣子 (大約4個小時吧),以下是我所參考的文件。

我遇到的問題:

  1. nginx + php5-fpm 只為特定 location 啟用 PHP :這部份卡了我好一陣子,後來是找到這篇:php – nginx projects in subfolders,參考裏面的作法去設定。大致是這樣:
    location /wiki {
        root /var/www/wiki/public;
    }
    
    location ~ /wiki/.+\.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_param  SCRIPT_FILENAME /var/www/wiki/public$fastcgi_script_name;
    }
    
  2. 無法啟動 vboxweb-service,出現 “vboxwebsrv: error: failed to initialize COM”:原本以為是沒加 dialout 群組的關係,後來根據某篇討論(網址不小心被我丟了)仔細推敲以後,發現是因為 VBOXWEB_USER 所指定的使用者沒有 HOME 資料夾,幫這個使用者建立 HOME 以後,就可以順利啟動 vboxweb-service 了。
  3. 無法登入 phpvirtualbox:這是我自己搞錯了,原本以為 config.php 裡指定的 username 跟 password 就是用來登入 phpvirtualbox 的帳號跟密碼,但後來才知道這組帳密是讓 phpvirtualbox 與 vboxweb-service 溝通用的,預設的 phpvirtualbox 帳密是 admin/admin。要修改的話,登入以後再變更就可以。
  4. RDP(Remote display) 不能用:這要先查到 virtualbox 的版本號碼,然後到Virtualbox download site下載對應的 .vbox-extpack 檔案,再用 vboxmanage extpack install 安裝。安裝完成以後,要重新啟動 vboxweb-service 與 vboxdrv 這兩個服務。最後用瀏覽器開啟 phpvirtualbox 頁面,去設定指定的 VM,就可以看到 Remote display 可以設定了。啟動 VM 以後,就可以用遠端桌面連線連上並看到這台 VM。連線所指定的 IP 是 host phpvirtualbox 這台機器的 IP,port 則是 3389。

另外,啟動所需要的光碟 ISO 檔案或是已經有打包好的 disk image 無法透過 phpvirtualbox 上傳,必須要事先透過 SSH、FTP 等傳輸協定放到 host 上。

其他參考資料 (與 nginx/php5-fpm 相關的):