2016年12月31日 星期六
[Go] 利用goquery + otto來分析網頁
via IFTTT
2016年12月29日 星期四
[Android] Retrofit + Protobuf + Wire
via IFTTT
2016年12月27日 星期二
[Android] 把Github當Maven repository用
via IFTTT
2016年12月11日 星期日
[Android] 在Android Studio取得certificate fingerprints
via IFTTT
2016年11月30日 星期三
[Android] Firebase + WebRTC on Android
via IFTTT
2016年11月4日 星期五
2016年11月1日 星期二
淺談Social Feed - 後端架構實作 [Server]
via IFTTT
2016年10月30日 星期日
淺談Social Feed - 多服務彙整式的social feed [Client]
via IFTTT
2016年10月21日 星期五
淺談Social Feed - 概念篇
via IFTTT
2016年10月18日 星期二
Account Kit samples for Swift
via IFTTT
2016年10月17日 星期一
在Ubuntu下mount box.com的內容
via IFTTT
2016年10月6日 星期四
[筆記][ios開發] 使用MapKit做一個簡單的飛行動畫
via IFTTT
2016年10月4日 星期二
2016年9月30日 星期五
[Android] 使用Retrofit如何避免Man in the middle攻擊
via IFTTT
2016年9月27日 星期二
使用AWS lambda和Github來提供中華職棒賽程資料
不知不覺的突然就多出了兩天颱風假, 這颱風實在很威, 乒乒乓乓的, 不過, 也沒做什麼, 時間就快過完了, 現在才想到, 還是來寫點什麼, 嚴格說來這些東西並不完全是颱風假時弄的, 只是拖得有點久
起因是, 之前(很久…追朔到去年)想寫個App, 需要用到中華職棒賽程的資料, 拖了很久一直沒真的去做, 斷斷續續的, 最近才先把資料這部分補齊, 首先需求是:
- 當月之後的賽程資料, 但中華職棒並沒有API, 只有(很爛)的網頁, 因此資料勢必得從網頁去解析
- 由Client app直接去解析html, 會比較麻煩(如果網站更新了, 就要更新App), 不是那麼可行
- 不想花錢(或不想花太多錢)弄一個server, 更不用說還要考慮Scaling
而賽程表這樣的資料的特性則是:
- 球季是3~10月
- 資料內容除週一(休賽)外, 幾乎每天都會變, 但不會一兩個小時或幾分鐘就變一次
- 變動的內容可能是:
- 比賽結束, 比數有更新
- 延賽或停賽
- 一個月才幾十場比賽而已, 基本上不太需要有search或query的功能, 依據月份分類也就足夠了
因此我採用的做法是:
- 利用AWS lambda定時解析中華職棒網站的資料
- 資料以json格式存在github (使用Github api)
- Client透過CDN去要這些json的raw content
賽程解析
這部分我是用Go + Goquery來寫的, source code在這邊: cpblschedule, 這code沒啥整理過, 光解析這堆亂七八糟的html就夠頭痛囉, 就沒啥整理
我做成了一個package, 因此要使用可用下列指令先安裝:
go get -u http://ift.tt/2d4uMIC
裡面也很簡單就一個function而已, 因此要使用可以參考:
import "http://ift.tt/2d4uMIC"
func main() {
matches, err := cpblschedule.ParseCPBLSchedule(year, month)
....
}
AWS Lambda
這邊就不介紹這東西是什麼了, 網路上文章一大堆, 基本上他是AWS一個severless的解決方案(這算廣告詞吧), 會使用這個的原因有二:
- 依我的用量應該是免費(事實證明, 其實還是要花點錢, 我忘了算網路傳輸的費用了, 不過這不多)
- 可以用Cloud watch排程觸發
不過有個小問題
**他不支援Go!!!!!**
而我上面那個解析的東東是go寫的, 那不就寫心酸的
所幸還有別的辦法,就是把程式編譯成執行檔, 然後用nodejs去包裝它, 不過這有點煩瑣, 所幸還有工具
apex, 這是讓你更簡單的去建立lambda function的工具, 而且他正好也可以幫你簡單做好上面所說的包裝
安裝及使用就看文件吧, 不特別說了, 但要如何用go寫一個lambda function handle呢?以下是範例:
import (
"encoding/json"
"http://ift.tt/2az06sB"
)
func main() {
apex.HandleFunc(func(event json.RawMessage, ctx *apex.Context) (interface{}, error) {
dosomething()
return nil, nil
})
}
Github as API source
資料既然變動不頻繁, 就用lambda定期產生然後把結果放到Github上就可了
Github API的Go的實做是Google放出來的go-github, 文件還蠻眼花撩亂的, 不過在這應用需要的API不多:
- client.Repositories.GetContents - 取得內容
- client.Repositories.CreateFile - 創立一個新檔
- client.Repositories.UpdateFile - 更新某個檔
之所以需要1的原因是要確認檔案是不是已經在repository裡面了, 如果沒有就用create, 如果有就拿SHA hash去更新內容
GetContents會把檔案內容一併給抓回來, 這可以用來在更新檔案前先比較, 如果不比較, 就算沒更動, API也會新增一個新的commit, 為了避免不要太誇張, 還是先比較一下好了
那之後client怎樣存取這些資料? 找到檔案, 選取raw就可以知道raw的url了, client每次就抓這個URL就好, 但為了避免過量地request湧到github, 因此透過一個CDN來存取可能會好一點
這時候就可以用RawGit, 這邊透過MaxCDN, 讓你可以去存取Github上的raw content, 而你的檔案的網址會是像這樣:
http://ift.tt/2d4vhm7
大致上就這樣
from Le murmure de Julian http://ift.tt/2dxgYCa
via IFTTT
2016年9月18日 星期日
2016年8月31日 星期三
[Blog] 替Jekyll的markdown加上簡易流程圖功能
對一個developer的blog來說, 流程圖似乎是蠻需要的, 比較能夠清楚來解釋一些東西, 但每個東西都轉圖檔還蠻麻煩的, 下面介紹一個有用的Jekyll plugin, 可以做到像下面這樣的效果:
第一例
第二例
這是利用一個叫做Jekyll-mermaid 來達成的
而這plugin其實也沒做很多事, 它是包裝了mermaid這個工具, 而mermaid這工具他是利用了ds.js來讓你用很簡單的方式來繪製流程, 以上面兩個例子為例
第一例
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
第二例
sequenceDiagram
participant John
participant Alice
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
所以你在markdown裡面只要加上
{ % mermaid % }
sequenceDiagram
participant John
participant Alice
Alice->>John: Hello John, how are you?
John-->>Alice: Great!
{ % endmermaid % }
他就會幫你render出相關的流程了
安裝方法
這邊以我自己blog的安裝方法來說明
- 把jekyll-mermaid.rb放到_plugins目錄去
- 在_config.yml加上 (這邊以6.0.0的mermaid為例):
mermaid:
src: 'http://ift.tt/2c49O8u'
- 還要在head.html加上css (要配合版面顏色), 可以用這個 : http://ift.tt/2ce9Blg
from Le murmure de Julian http://ift.tt/2c49o2a
via IFTTT
2016年8月17日 星期三
[筆記]製作自己的Icon font
承續上篇的用icon font來製作圖示, 之前所提到的都是利用現成的icon font, 但似乎大部分的icon font都沒有像material icon有支援ligatures, 沒支援的話, 在xcode裡面就無法像上一篇一樣, 直接在UI designer顯示對應的圖示, 另外如果需要使用自己的圖示呢?其實是有方法用SVG圖檔來製作自己的icon font的, 這篇就來介紹兩種用SVG圖檔製作一個有ligatures支援的字型檔
grunt-webfont
第一個方法就是利用grunt-webfont, Grunt是一個前端常用的建構工具, 而grunt-webfont是一個用來產生字型的task
安裝相關工具
由於需要使用Grunt, node.js是必須的, 另外由於需要使用到fontforge, 所以python也是必須的, 雖然說grunt-webfont也可以純nodejs的module來產生字型, 但那並無法支援ligatures, 所以fontforge是一定需要的
用npm i grunt --global
來安裝grunt
製作字型
- 建立一個空的目錄
- 在這個目錄執行
npm init
來產生package.json
npm i grunt-webfont --save
來安裝grunt-webfont並且把這個dependency 加到package.json
- 建立一個svg子目錄(目錄名稱隨你高興, 這邊以svg當例子), 把所有圖示的svg檔案全部放到這目錄去
- 建立Gruntfile.js , 這檔案就像是Makefile, 或像是build.gradle這樣的角色, 內容就像下面
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
webfont: {
icons: {
src: 'svg/*.svg',
dest: 'build/fonts',
options: {
engine: 'fontforge',
htmlDemo: true,
fontHeight: 96,
normalize: false,
ascent: 84,
descent: 12,
font: 'octicon',
fontFamilyName: 'octicon',
types: 'ttf',
ligatures: true,
startCodepoint: 0xF101
}
}
},
clean: [
'build/fonts/*'
]
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-webfont');
grunt.registerTask('font', ['clean', 'webfont']);
grunt.registerTask('default', [ 'font']);
};
這邊最主要也最重要的task就是webfont這個, 這裡面src
是svg檔的目錄, dest
是字型輸出的目錄, engine的部分指名fontforge, ligatures設定必須要是true(產生的字型的ligature的名字其實就是沿用svg的檔名)
建立好這個檔後執行grunt
即可
icomoon
上面的方法還是有點麻煩, 蠻手動的, 還有一個更方便的工具就是icomoon, 這東西方便更多, 它是一個相當強大的工具
從畫面上看, 它其實很簡單操作, 選定你所需要的圖示後, 按右下角的Generate Font
即可, 除了你可以自己import自己的svg檔案外, 它也提供很多付費跟免費的圖示供選用:
按下Generate Font
後, 並不會馬上讓你下載字型回家, 它會先讓你檢視字型將會包含的圖示, 這邊有件事很重要, 左上角有個fi
圖示(參照下圖), 按下去後, 下面的圖示下面會多一個fi的欄位, 這就是讓你設定這些圖示的ligature的, 如果需要一個有支援ligature的字型, 就需要去設定這邊
所有都沒問題後按下右下角的Download
就沒問題了
from Le murmure de Julian http://ift.tt/2bBpZvO
via IFTTT
2016年8月4日 星期四
[iOS開發筆記] 使用Icon font來節省圖示空間
寫App有一個讓人頭痛的是App大小的問題, 而這大小有部分是由App裡面所用的圖所貢獻, 為了減少這部份消耗掉的資源, 不管是用較大壓縮率的格式來壓縮圖檔, 還是其他, 大家都想盡辦法想解決這問題, 現在由於平面化的UI設計, 使得又有不錯的方法來解決這問題, 平面化UI設計的特色是大部分的圖檔都是單色而非五顏六色, 這使得用向量圖, 甚至用字型來解決這問題變得可行
免費的圖標字型(icon font)
把所有的向量圖示變成字型檔可以節省不少空間, 以流行的Font Awesome 來說, 它包含了634個圖標, 卻只佔了153KB, 這在以往可能是不到十個圖標的檔案就會達到的大小, 相較之下節省了不少空間, 像這樣開放的圖示字型, 可以找到不少:
- Font Awesome : 蠻流行的一個開放icon組, 提供了ttf, woff等字型檔格式
- Google material icons : Google開放源碼的免費icon組, 它不只提供Android, iOS可使用的圖檔外, 也提供了字型檔的部分, 而且它的字型檔支援了Ligatures (後面會再提到它好用的地方),這也使得它比Font Awesome來的好用
- Weather Icons : 顧名思義, 這提供了222個可以用於表示天氣的icons, 不過對於風向的表示的部分, 它是用同一個圖示只是在web上利用css旋轉來顯示不同方向的風, 這一點應用到App上的話, 我是還沒找到比較適合表達的方式
- Octicons : 由GitHub開源出來的圖標字型, 圖標不多, 但自己新增應該蠻方便的(自己增加svg檔用grunt去build)
除了這些之外, 應該還可以找到不少免費的圖標字型(icon font)可以用, IconFontKit這邊就列了不少(它也整合了)可用的圖標字型
使用這些, 除了可以節省app的大小, 也可以省下不少設計圖標的時間, 但也不是沒缺點, 因為是字型的關係, 它每一個icon都是對應到一個unicode字元, 這字元大多數跟icon的形狀沒關係, 也就不是那麼好對應, 通常都要查一下對照表找出字元碼
利用現成的framework整合
要在iOS上使用這些圖標字型(icon font)的方式好幾種,寫程式去load字型是一種, 當然就有不少大德, 寫好包裝可以讓你用cocoapods或是cathage直接引入, 這邊有幾個不錯的:
- IconFontKit : 這個整合應該是最完整的, 以Objective C寫的, 缺點是, 整合太多了, 反而變肥了
- FontAwesome.swift : 這一個是針對FontAwesome, 還蠻輕量的, 以Swift時做的, 類似的有用Objective C寫的FontAwesomeKit, 不過比較起來還是FontAwesome.swift比較輕量, 如果用的只是FontAwesome, 那還是這個好
- FontWeather.swift : 老王賣瓜一下, 因為想寫的東西剛好有需要用到Weather Icons, 所以我就用了FontAwesome.swift 的方式做了包裝
用這些現成的framework的好處是, 一來減去自己手動包裝字型進app的複雜度, 二來是, 這些已經幫你定義好一些對應圖標的常數, 讓你用比較方便的方式而不是記憶unicode字元來對應這些圖標
但它也是有缺點的, 大部分這些的作法都是runtime才去載入跟註冊字型, 因此你必須是在程式內設定你的UILabel, UIButton的字型, 無法事先就在Interface Builder做預覽, 所以個人比較喜歡的方式就是自己動手來
手動在xcode上使用自訂字型
自己手動加的好處就是, Interface Builder上就可以套用, 直接就可以看到結果, 但就是稍微繁瑣了一點
首先, 要把字型檔拖入你的Project裡面:
接著打開Info.plist, 加上一個新的項目叫做“Fonts provided by application”
這個是一個陣列(Array), 它的內容就是你要加入的字型檔檔名, 把你要加的每一個都列進去
接著, 在Interface Builder裡你所要使用icon font的地方, 比如說UILabel設定你的字型, 原本的字型是設定為“System”, 把它改成“Custom”, 並選定你所需要的字型名稱, 例如FontAwesome, 要注意的是, 字型名稱不一定等同於你字型檔的名字:
接下來在Text的部分輸入這個圖示的代表的Unicode字元就好, 不是Unicode碼, 而是那個字元本身, 這挺不方便的, 可能用copy paste的才有辦法, 這是這個方法最大的缺點
這問題還是有方法克服, 這也就是前面為何提到會比較推薦使用Google material icons而不是Font Awesome , 這原因就是Ligatures
Ligatures
Ligatures是一個字型上蠻方便的特色的, 關於Ligatures可以先看一下這篇, 這是在Google material icons提到的一篇文章:
剛剛提到的一個很大的缺點是, 你要知道圖示對應的Unicode碼才可以在你的UI上顯示你想要的圖示, 這相當不方便, 尤其那些Unicode碼可能根本完全不代表任何意義
比較人性點的作法是當你想要一個圖示代表藍芽, 用bluetooth就可以找到對應圖示, 而Ligatures就是一個這樣的存在
我們先來看看, 如果使用沒有而Ligatures的FontAwesome, 你在Text打上“Contacts”會是怎樣一個情形?
它會直接一字不漏的呈現“Contacts”,這還是因為FontAwesome有包含原本英數字字型在裡面, 有些其他的自行更慘, 根本就是一片白
讓我們再看看用Google material icons的字型,同樣的東西會有什麼結果
因為這個字型有支援Ligatures, 所以在這邊contacts就會被直接代換成它對應的圖示了, 我們就不用寄那種完全看不懂的unicode碼了
但大部分的字型其實也都沒有, 所以自訂字型該怎做?那就留待之後研究了
from Le murmure de Julian http://ift.tt/2aVJ1ya
via IFTTT
2016年7月5日 星期二
[iOS開發筆記] 模糊背景
去年為了參加WWDC, 開始練了Swift, 寫了兩個library, 不過好像一直都沒寫過完整的App, 連UI好像都沒真的去刻過(去年寫的東西跟UI比較無關), 因此最近利用了一些時間開始了個side project, 做side project就常常會把時間花在一些枝微末節的地方, 比如說, 為了做一個像Android那樣的Floating action button, 去找來一個現成的3rd party lib - yoavlt/LiquidFloatingActionButton , 那個像水珠一樣突出去的效果, 我還蠻愛的:
但缺點是, 後面缺一個擋住背景元件的, 以致於要去點伸上來的小按鈕容易誤按後面的元件, 因此就想自己改一個後面多一個overlay的版本, 當然也不想隨便貼一張白白的就交差, 起碼要像這樣:
這邊不是單純蓋一個深色半透明的背景而已, 還需要作一點模糊的部分
在iOS上(iOS8 之後), 作這樣的東西很簡單, 只要利用UIVisualEffectView和UIBlurEffect這兩個東西, 寫法很簡單:
let blurEffect = UIBlurEffect(style: .Dark)
let uiEffectView = UIVisualEffectView(effect: blurEffect)
uiEffectView.frame = overlayView.bounds
overlayView.addSubview(uiEffectView)
UIBlurEffect有三種樣式, Dark, Light, 和ExtraLight, 上面的範例是Dark, 蠻適合用在這地方的, 利用這個方法就可以不用自己擷取screenshot再算模糊化了
from Le murmure de Julian http://ift.tt/29fvPOb
via IFTTT
2016年6月18日 星期六
[Golang] 一個簡單的Mongo db proxy
之前被Parse搞的半死, 一直很好奇它的API到Mongodb的request之間到底是怎樣的對應
要弄清楚這個其實也不難, 把Mongodb的profiler全打開去看log就好了(db.setProfilingLevel(2)
), 但這也是有缺點, profiler會寫到system.profile
這個collection去, 而它是固定大小, 不能無限制的放, 再加上它還要多寫入這段, 多多少少影響效能
我需要的是一個從外部來觀察的工具, 不會影響到DB本身, 並且也可以將網路本身所花費的時間也包含進去, 所以想到的是在中間插一個proxy server
在現成的工具找到一個叫MonoDB Proxy的工具, 這是用nodejs寫的, 勉強可以, 也證明了這個方法是可行的, 但這工具雖然有做到代理這部份, 但在log部分, 由於它並未解析bson, 所以詳細的內容並不好看, 所以就自己來寫一個
功能需求
- 支援mongodb wire protocol, 而不是只是單純的轉送資料
- 印出request跟response內JSON的內容
- 要能夠知道每個request所需要的時間(含網路)
成品
最後寫出的的成品在這: http://ift.tt/1UVnl1z
整個還蠻簡單的:
- wire.go 實作wire protocol
- proxy.go 實作從client收資料並轉寫到server端
- cmd/mp/main.go command line主程式的部分
使用方法
這個工具是用Go寫的, 所以使用之前需要先安裝go ##### 安裝 go get julianshen/mongoproxy/mp
這個步驟做完後, 就可以把mp這個指令裝好了, 確定 $GOPATH/bin是在你路徑內, mp這個檔也是在那邊
使用
mp --port=6001 --remote=mydb:27017 --response
其中:
- port是你這個proxy server的服務點
- remote是遠端的mongodb (host:port)
- Reponse是需不需要log回傳的部分
實作Wire protocol
本來覺得Wire protocol會蠻複雜的, 結果, 其實是蠻簡單的
所有的wire protocol request都會有一個標準的表頭:
struct MsgHeader {
int32 messageLength; // total message size, including this
int32 requestID; // identifier for this message
int32 responseTo; // requestID from the original request
// (used in responses from db)
int32 opCode; // request type - see table below
}
對應golang, 我定義成這樣: go type MsgHeader struct { MessageLength int32 // total message size, including this RequestID int32 // identifier for this message ResponseTo int32 // requestID from the original request // (used in responses from db) Opcode // request type - see table below }
因為一開始就可以讀到整個訊息長度的, 所以就蠻好解析的, wire protocol的實作我是有參考了dvara, 本來是有想拿它的code來改, 但看了一下發現它也沒完整實作wire protocol, 秉著自己也來了解一下這部份的想法, 就重頭自己刻了
跟dvara不同的地方是, 我用go的binary package來讀header而非自己刻一個, binary.Read的確是一個蠻好用的工具, 用底下的code就可以讀出header這個資料結構:
h := MsgHeader{}
err := binary.Read(r, binary.LittleEndian, &h)
另外, 除header外, 各request的所帶的欄位各自不同, 這部份的作法就是定好各個所需的資料結構, 用reflection的方式來讀取各相關資料:
v := reflect.ValueOf(req)
v = v.Elem()
// 根據資料結構內定義的每個欄位用相關的方法讀取
for i := 0; i < v.NumField(); i++ {
f := v.Field(i)
t := f.Type()
if bytesRead == int(h.MessageLength) {
break
} else if bytesRead > int(h.MessageLength) {
return nil, ErrorWrongLen
}
switch {
case t == reflect.TypeOf((bson.D)(nil)):
d, n, e := readDoc(bufferReader)
解析bson
這部份就沒再重新造輪子了, 直接用golang著名的mongodb driver mgo裡的bson lib: http://ift.tt/1UVniD7 , 這個bson lib已經寫的很不錯了, 直接拿來用即可
在這個package內, 泛用的bson資料結構有兩種: bson.M
和 bson.D
這兩個是不同用途的,仔細看一下M跟D的定義:
type M map[string]interface{}
和
type D []DocElem
如果你是要把解析出的資料用map來操作, M是蠻方便的, 一開始我也是依著之前我寫相關的東西的習慣用M, 不過這邊卻是不可以用M的, 這也是我碰到bug的地方
由於M解析出的是Map, 所以每個field的順序它並沒記住, 但偏偏在wire protocol裡, 尤其是 $cmd, 順序是重要的, 所以Unmarshal出的M再Marshal回去, 順序可能不是原本的順序了, 而這在這個proxy應用上, client寫什麼東西過來就要寫什麼到server才不至於出錯
測試和Debug
我是用Wireshark來驗證我的實作有沒問題, Wireshark預設會把到27017 port的資料解析成wire protocol的內容供閱讀, 當然也可以自己手動請它解析
其他應用
這樣的proxy應該可以不只應用在debug, 像是Parse open source的dvara, 它也是利用了proxy來做connection pooling, 應該也可以用在request routing和caching的應用上
from Le murmure de Julian http://ift.tt/1UDwOvO
via IFTTT
2016年6月16日 星期四
又再把Blog搬家了
一直把Blog當作紀錄一些事情用的地方, 想法, 或是學習過的一些東西, 雖然不是很頻繁的在寫, 不過也寫了好幾年了, 從以前到現在, 換過好幾次平台, 從自己架的wordpress, 到posterous, blogger, 一直到現在用的Tumblr, 這中間最喜歡的還是posterous, 不過它已經不存在了, 而Tumblr也用了好一陣子了, 雖然加減湊合著用好像還OK, 不過編輯上也一直不是那麼順手, 尤其是貼source code, 因此也一直想把它換掉
那 medium 呢? medium我還沒很有動力去搞懂它, 會在上面看一些文章沒錯, 但把Blog移到上面去, 好像也沒啥動力
考慮了好一陣子, 不過最後還是把整個Blog遷移了, 反正現在有一個自己的domain (blog.jln.co), 搬家不用改地址!
遷移之前, 想了幾個我的需求:
需求
- 有方便的編輯器可以用, 最好可以支援mark down, 打html有點麻煩, WYSIWYG編輯器通常效率也不高
- 貼code好貼, 也方便閱讀
- 模版好編輯, 至少我要能知道怎麼改模版
- 好預覽
- 能同步到各個social network, 至少Facebook, Twitter, 也能同步到我原本的Tumblr和blogger
- 要能夠友善支援Open graph和Twitter card, 對每次share的FB的醜醜文字版型實在不喜歡
- 好轉移, 好備份, 有版本控制更好!
解決方案
現成的blog service, 好的什麼都要錢(ghost, wordpress), 免費的大概也都被我用過了, 自己架, 又得管server, 後來想想, blog都是靜態的網頁, 也真的不需要一個很複雜的系統, 這時候就想到一個解決方案, 那就是Github
Github有一個Github pages的服務, 可以讓你host你的靜態網頁, 所以只要有一個方法可以把Blog轉成靜態網頁就可以了, 這個還蠻Geek的方式感覺就蠻適合我的
找到了幾個方法轉blog:
Octopress是已經完結不再維護了, 所以就Jekyll和Hexo兩個在抉擇, 兩者都有蠻多的主題跟模版功能, 也有不少plugins
Hexo是台灣精品, 而且是nodekjs寫的, 語言上我比較熟, 要改比較好改,而Jekyll是Ruby寫的, 我跟Ruby很不熟, 但它跟Github pages結合緊密, 而且已經有tool可以從Tumblr移轉內容過來了, 最後我選擇了Jekyll, 選擇Jekyll的原因是
- Github page直接原生支援, 只要把md檔push上去後, 就會自動產生網頁(不過後來我還是先在自己電腦產生完再push, 後面再說原因)
- 找到一個適合的也不太難改的theme, 就懶得去翻Hexo的了, 而且找到的那個對Open graph和Twitter card的支援也不錯, 不用改太多
- 原生的Tumblr import tool
不過整個也花了不少個晚上修改, 又花了一個晚上才能寫完這篇紀錄 @@
安裝Jekyll
網路上可以找到很多安裝Jekyll的文章, 這邊就不說太多, 我試了兩個不同的作法:
作法 1
這是最基本的作法
- 先用
gem install jekyll
安裝jekyll - 用
jekyll new myblog
在myblog目錄產生一個基本的網站 - 用
jekyll s
會在 localhost:4000 開啟一個服務, 這時就透過browser可以看看你的blog長怎樣了
這方法的缺點是, 套用主題時, 還得改一堆東西, 有點麻煩
作法 2
- 一樣先裝jekyll
- 找到一個人家做好的theme (我是用hpstr這個), clone下來改, 因為大部分的都已經把他用的plugin之類的都先寫好在config了, 就省不少事, 建議找theme要注意其對應的Jekyll版本, 像我裝的Jekyll是3, 所以找的是適合3的
- 用
gem install bundler
安裝bundler - 用
bundle install
來安裝相關的plugins - 用
jekyll s
看結果
我後來採用的是作法2, 踩在人家的肩膀上比較快 :P
設定github pages
這部份沒什麼難度, 創建一個專案名稱叫做 你的名字.github.com , 把相關檔案放到這邊去就行了
Github pages支援兩種方式, 一種是純粹的靜態網頁, 就是把html跟其他相關檔案丟到這邊就好(master branch), 另一種方式就是Jekyll, Jekyll的部分只要把前一個動作的檔案放進來就好, 它會自己幫你產生對應的靜態網頁
不過第二種方法的缺點是因為Github pages只支援幾種Jekyll plugins, 而且不支援自訂的plugins(放在 _plugins 目錄下的), 就算你在你自己電腦裡面跑完把產生後的 _site 目錄一起放上去也是沒用的
從Tumblr匯入之前文章
這部份沒太複雜, 是透過jekyll-import
安裝jekyll-import一樣可以透過gem install
來安裝, 但建議不要這樣做, 是因為他用的Tumblr API是JSONP但程式卻將它當一般JSON在解碼, 會出錯, 這部份在最新版的code有解決(害我還去追了source code), 所以抓source回來自己build比較安全
裝好之後執行:
#!/bin/sh
ruby -rubygems -e 'require "jekyll-import";
JekyllImport::Importers::Tumblr.run({
"url" => "http://blog.jln.co",
"format" => "html", # or "md"
"grab_images" => true, # whether to download images as well.
"add_highlights" => false, # whether to wrap code blocks (indented 4 spaces) in a Liquid "highlight" tag
"rewrite_urls" => false # whether to write pages that redirect from the old Tumblr paths to the new Jekyll paths
})'
它就會把文章抓回到 _post的目錄下
Open graph & Twitter card
如果把內容分享到Facebook或是Twitter上只有短短的文字, 不是很好看, 所以希望在這部份能夠至少加上一張圖
hpstr這個theme已經有在 ** _includes/head.html ** 寫好OG跟Twitter card相關的tag了, 圖片(og:image)的部分, 他的規則/順序是:
- Front matter 裡面設定的
- _config.xml 裡的logo設定
我希望是更自動一點, 而不是自己去設定, 所以找到一個plugin : jekyll-auto-image, 這個plugin聰明一點, 他的順序是:
- 內文內出現的第一張圖
- _config.xml 裡面設定的圖
但這樣還稍嫌單調一些, 我想要達成的是:
- 內文內出現的第一張圖
- 根據標籤選的對應的圖(比如說把我go語言相關的設成Gopher的圖片), 這樣不會每個都一樣, 比較有變化一點
- 預設的圖
再加上auto image裡面關於image的設定跟我用的theme有所衝突, 這就需要把原本的auto image改一下了
我新增一個rb檔, 放在 ** _plugins **目錄下, 內容如下:
require "jekyll"
module Jekyll
class AutoImageGenerator < Generator
def generate(site)
@site = site
site.pages.each do |page|
img = get_image(page)
page.data['image1'] = img if img
end
# Now do the same with posts
site.posts.docs.each do |post|
#puts "hola"
#puts Jekyll::VERSION
#puts post.class
#puts post.inspect
#puts post.data.inspect
#puts "-----"
#puts post.output
#puts "----"
img = get_image(post)
post.data['thumb'] = img if img
end
end # generate
def get_image(page)
if page.data['thumb']
return page.data['thumb']
end
# convert the contents to html, and extract the first <img src="" apearance
# I know, it's not efficient, but rather easy to implement :)
if page.class == Jekyll::Document # for jekyll 3.0 posts & collections
htmled = Jekyll::Renderer.new(@site, page, @site.site_payload).convert(page.content)
else
htmled = page.transform # for jekyll 2.x pages
end
img_url = htmled.match(/<img.*\ssrc=[\"\']([\S.]+)[\"\']/i)
return img_url[1] if img_url != nil
tags = page.data['tags']
imagemap = @site.config['imagemap']
if tags != nil && imagemap != nil
tags.each do |t|
if imagemap[t.downcase] != nil
url = imagemap[t.downcase]
if !url.match(/^http/)
url = '/images/' + url
end
return url
end
end
end
return @site.config['logo'] if @site.config['logo'] != nil
return nil
end
end # class
end # module
這版就有達到我想要的目的, 但這不代表問題解了, 反而是問題的開始, 前面有說過github只支援特定的plugins, 所以怎樣這段改過的plugin都不會被它執行到, 唯一能做的解法就是改用靜態網頁的方式
jekyll產生的靜態網頁都放在 _site 目錄下, 所以真的要進master branch只有這裡面的, 其他都不用, 所以要先在本地端用 jekyll b
去產生相關的靜態網頁到 _site裡再上傳
那怎測試呢? 一開始為了測試方便我都以本地端的server做測試, 但如果要測試Facebook share的話則需要能夠讓Facebook 連過來抓取網頁來分析, 但這點在區網不是很方便, 所以就帶入了 ngrok 這個工具, 這工具就是提供給你一個外部的網址, 你在本地端跑了ngrok這程式後, 就有一個可以對應你本地端的server, 還蠻好用的
自動發布到社群網站
這當然是用ifttt囉, 因為有用到jekyll-feed這個plugin, 所以會產生一個feed.xml的檔案, 所以就利用ifttt的feed channel來設定觸發
結語
本來明明不想花太多功夫的, 結果不但花了很多功夫, 還花了一頁來紀錄這篇 orz
from Le murmure de Julian http://ift.tt/1OszPie
via IFTTT
2016年6月15日 星期三
Hey blog test
This is to test my own blog
header 1
header 2
console.log("111")
test test
Header1
Header2
Header3
from Le murmure de Julian http://ift.tt/1UW76yv
via IFTTT
Hey blog test
This is to test my own blog
header 1
header 2
console.log("111")
test test
Header1
Header2
Header3
from Le murmure de Julian http://ift.tt/1UW76yv
via IFTTT