What have you found for these years?

2011-09-03

撰寫 ruby 好似無所不能..?

事情是這樣的...

我覺得在發佈一個新的版本之後,最好也同時發佈一份 ann (announcement)
而理想上的 ann 內容大概是 README 的一小部份加上 CHANGES 的一小部份,
也就是最新的那個版本的更新內容。

可以想像這是一個非常容易自動化的動作,但其實事情沒那麼單純。首先是
我怎麼確認我要抓取哪些段落? DESCRIPTION? INSTALLATION?
LICENSE? 要怎麼挑出來?

作法是觀察 README 的格式,用 regexp 想辦法切出 name => paragraph 的
hash table. 接下來我就可以任意安排段落,調整 ann 的輸出格式。
除了 README 需要這樣切外,CHANGES 也同樣要。

之前這邊很複雜,因為我同時想相容 markdown 和 rdoc. 現在我已經徹底
放棄 rdoc 了,所以就變得單純些。放棄 rdoc 的原因是 yard 本身也支援
markdown, 那我何苦使用拿來寫文件不太方便的 rdoc?

得到方便的 hash table 後,就可以任意組織、調整段落的順序,看看哪種
ann 看起來能夠讓第一次看到的人和已經看過很多次的人,都能快速找到
他想看的段落。前者會希望知道「這是什麼」,而後者會希望知道「改變了
什麼」。

還不只這樣。

由於我是要放在 blog 上,也就是嵌入的 html. 這樣原本的 h1 不太適合繼續
是 h1. 因此我會希望每個 hn 都是 h(n+1), 也就是 h1 改 h2, h2 改 h3.
以 markdown 為例,就是前面多加一個 #

最後,由於 kramdown (markdown => html) 會把所有 hn 都加上 id,
這我也要拿掉,原因同上。可能同一頁裡面就有不同的 ann, 如果 id 相同的
話怎麼辦?從簡就好了。

有點複雜,自己也沒確定要怎麼做比較好,因此我每一次 ann 都寫得有點
不同。最近由於頻繁在開發,就常常需要放新版本,因此開始覺得不堪其擾。
終於在今天下午,由於一些小原因,開始進行程式化的動作...

* * *

或許只是單純因為我很熟 ruby 了,我覺得我可以非常專注在「尋找問題」
並「解決問題」這個層面上,而不是思索如果我要達到這件事,我應該怎麼
做的層次上。當然,這也很可能是因為我的思緒已被 ruby 侷限住了。總之,
整個作業大概是這樣進行的...

首先讀出 README 的內容,然後想一個 regexp 去切出我要的段落。
找到正確的切法之後,則是把切好的內容做成一個 hash table.

其實 regexp 不太是個想出來就立刻可以用的東西,大概都要前後試個幾次,
才能確定怎麼寫比較好。這時候當然又是拿出 rib 啦~ 只要加個

require 'rib/config'
Rib.anchor Gemgem
就可以慢慢試到自己滿意為止... anchor 在 Gemgem 上是因為那是我拿來處理
這些有的沒的的 module.

做到這裡,可以順便把之前抓取 DESCRIPTION 的程式改掉,因為現在
既然有比較 general 的版本了,就沒必要用之前針對 DESCRIPTION 寫的。
同樣的事,也要對 CHANGES 做一次,因為兩個格式不完全一樣。

接著由於我已經抓出我要的所有 paragraph, 我就可以在每個 header 前面
多加一個 #, 這樣就解決 h(n+1) 的問題。唔,然後有些 header 希望再做
一些小小的調整,例如第一個 header 希望加個 link. 同樣用 regexp 稍微
試一下就行了,畢竟所有資料都能輕易 travel 了,還有什麼不能做?

最後要把 markdown 轉成 html, 我自己是用 kramdown. 一個作法是由於
他也是 ruby 寫成的,我可以從記憶體裡面叫出來用。不過我懶得去查到底
要怎麼用,所以還是用平常習慣的方式去用他 -- UNIX command.
stdin 輸入 markdown, 他會 stdout 輸出 html.

在 ruby 裡就 IO.popen, 灌入我調整好的 markdown, 然後讀出 html,
接著再餵給偉大的 nokogiri... 我講真的,這可能是我最喜歡的 gem 之一。
不只是穩而且快,他的介面非常好用,不是其他 html/xml parser 可以比的...
除了 css 和 xpatch 可以很方便地搜尋 node 外,也可以把整個 tree 看成
一個很大的 ruby hash 去操作。

簡單地說,nokogiri 的介面設計得非常符合 ruby 的使用習慣。絕不是...
可以比擬的。以下省略數百字抱怨。

接著當然也是一樣,直接用 Rib.anchor html 去看要怎麼刪除所有的 id,
因為 nokogiri 其實我沒那麼常用,用法記不太起來。這邊我是用 XML 去
parse, 然後輸出 html. 因為 html 去 parse 會產生一些 html 的東西,而
kramdown 輸出的 html 其實完全符合 xml. 但是輸出又要用 html 模式,
因為輸出 xml 格式的話會包含 <?xml 那些東西。

也就是說,就跟上次做投影片一樣,用 rib + nokogiri 去調整輸出格式...
簡單的東西都是靠直覺寫,不夠熟悉的東西,就是用非常 interactive 的
方式慢慢去試,試到對為止... 自己其實不用動太多腦去思考解決的「方法」,
只要思考要怎麼解決即可。

大概是這樣吧。成果在 gemgem.rb, 用在所有我管理的 gem 上。
> rake
rake ann:html               # Generate ann html
rake ann:md                 # Generate ann markdown
rake clean                  # Remove ignored files
rake doc                    # Generate rdoc
rake gem:build              # Build gem
rake gem:install            # Install gem
rake gem:release            # Release gem
rake gem:spec               # Generate gemspec
rake test                   # Run tests in memory
rake test:shell[RUBY_OPTS]  # Run tests with shell

0 retries:

Post a Comment

All texts are licensed under CC Attribution 3.0