顯示具有 plugin 標籤的文章。 顯示所有文章
顯示具有 plugin 標籤的文章。 顯示所有文章

7/28/2007

Active Scaffold Upload Branch

我真的被 rails plugin 嚇到了。這幾天試著做 web development, 大家應該都知道我跟這個領域不太熟吧?可是,忽然間我覺得,這樣幾乎什麼都能做了。三個 plugin, 依照我嘗試的時間順序:

1. FileColumn
2. LoginGenerator
3. Active Scaffold Upload Branch

由於一開始使用 FileColumn 碰到了點問題,所以我有稍微 trace 他的 code, 基本上這個 plugin 我覺得算是小玩具,很簡單的小功能,居然沒內建到 rails 中我覺得有點奇怪。畢竟 binary file 一般都是存到 file system 而非 db 中吧?搭配 rmagick 的感覺還算不錯,可惜 helper 似乎不太健全,雖然堪用了。

接下來碰到需要 auth 的部份,懶得自己寫,因為只是小地方需要,並不真正屬於我目前所要寫的東西的一部份。重新發明輪子我覺得不是問題,但如果只是想到旁邊的便利商店買罐飲料卻因此得重新發明輪子,呃,這是成本考量與優先序的問題啊。

於是試了一下 LoginGenerator, 發現﹍。嗯,還真的是很方便。雖然他是個 gem, 但是 generate 出來之後就沒有 dependency 了,而且要自己改什麼也都很容易。於是 auth 的部份也解決了,畢竟我只是要最簡單的 auth.(雖然他安全性做得如何我就不清楚了,沒有仔細看他的程式碼)

到這裡,我都還不覺得怎麼樣。第三個 ActiveScaffold 就真的嚇到我了。因為那真的是非常完整的實做。rails 內建的 scaffold 實在陽春過頭,(儘管陽春過頭了,第一次看到還是著實相當驚豔,現在有種鄉巴佬進城的感覺)除非是最簡單的資料,不然碰上 association 或是 file column, 全部都沒辦法應付。當然,我是可以自己去改自己去擴充,但同樣是成本與優先序的問題,一定得需要用別人的東西。

先是找到了 scaffold extensions, 其實我是比較欣賞這種模式。不要產生一堆程式碼,要改什麼,利用 ruby 的 dynamic 機制就好了。只是他文件實在不多,看來看去要擴充好像還滿有難度的,而且雖然號稱支援 association, 卻是﹍。我只能說真的很難用,不過是多個 link 出來罷了,是比沒有好啦,但是應該會邊用邊想打人吧?

接下來我乾脆直接 google file column + scaffold 算了。就找到了 Active Scaffold Upload Branch, which is evolved from ajax scaffold. 他的 file column 範例,呃,問題一大堆,有些根本就寫錯了,害我試半天試不出來,有點火大。想說調查一下他的 form 到底是怎麼回事,卻發現他居然整個是用 ajax 寫的,所以 rails 有錯誤不見得會回傳,而且 form 也因為動態產生而無法顯示!

幸好有 firefox 的 web developer 這個 add-on, 裝了很久卻很少在用,帶有僥倖的心情找了一下,發現他的 display 功能非常強大,舉凡頁面上出得來的,全部都能顯示。然後才發現,active scaffold 額外產生了一個 record 去存,這樣的話下面這一行就明顯不正確了!
<%= file_column_field 'entry', 'file' %>
我還是頭一次看到範例有這麼嚴重錯誤的,太相信他結果找錯誤找半天。:( 害我又一直懷疑到 file column 和 rmagick 上,看半天覺得應該沒錯才對。而且這一個 method 也不知道是幹嘛的:
 def file_form_column(record, input_name)
  file_column_field 'record', :file
 end
不是明明就有 partial 了嗎?還是這可以取代 partial? 存疑,不管了。

總而言之,現在不只是 file column 的 image upload 沒問題,要自訂欄位也沒問題,版面上又相當漂亮,不會因為拉扯而變形,排序、搜尋等等,也都相當不錯。最重要的是,他的 association 模式跟我所需要的幾乎完全一致!可以同步修改,可以隱藏,可以追加,所有基本功能似乎都有了。

本來昏昏欲睡的我,看到這邊實在是很興奮,這東西就算要直接拿來用,都夠格了。而且我相信有什麼不足的話,要修改也容易得很。這有一大半要感謝 Ruby :)

老實講,我真想知道硬幹派的 php programmer 看到這些會有什麼想法。一個 phpBB 我看隨便寫一寫說不定就跑出來了﹍。搞不好再過不久,寫網站就真的是只要呼叫 => 修改 => 呼叫 => 修改就結束了。這還真的是很恐怖的一件事。不過同時這也代表著,我們應該把眼光放遠點了。來做個 YouOS 吧!! XD

==

最後我還想講一件事,就是寫程式寫這麼久,從來沒碰過幾次時程預估是太長的。但寫 rails 這兩次,很明顯我都估太長了。而且縮短的程度也是非常的多!我想除了要感謝 library 的強大外,雖然我還沒引進 unit test, 只用了人工 test, debug 難度就已經沒有很高了,大部份的錯誤都不會很難抓。當然,這有一個很大的原因是我現在在寫的東西難度很低,可是在這麼不熟悉的情況下,我覺得能夠這樣就真的是非常厲害的了。

另一方面其實我是覺得寫 Flash 有趣得多,難度也高得多。但 Flash debug 真的是會讓人想抓狂的一件事﹍。不知道有沒有什麼好用的 debug tool? 不然寫 Flash 永遠進度落後實在是很煩。

==

script/plugin install http://opensvn.csie.org/rails_file_column/plugins/file_column/trunk

gem install login_generator
script/generate login

svn export http://activescaffold.googlecode.com/svn/branches/upload vendor/plugins/active_scaffold_upload

有時候真不知道用 rubygems 好還是 rails plugin 好,但我想比較不穩定的東西,用後者應該是會好很多吧?self contained 這種事,有時候還滿重要的。

鄉巴佬全文完。

2007.07.28

5/05/2007

script/plugin

well, 由於我跟 Rails 不熟,所以很多地方只能憑空臆測,如果有誤也望請指點。很多跟 Rails 有直接關係的細節我也難以深究,所以大概只能從 Ruby 的角度看下去。總而言之呢,Rails 的 plugin 比起 rubygems 還更要簡單得多,根本沒有任何需要設定的部份,只要把目錄開好就可以輕易使用了。目錄結構大約是:

init.rb
install.rb
lib/*.rb
test/*.rb

init.rb 是 Rails 在 load up 時會執行的部份,所以 plugin 要把 init up 的程式碼放到這邊,例如最常見的恐怕是:
ActiveRecord::Base.send(:include, OOO)
這邊使用 send 而不是直接用 . 的緣故是 include 本身是 private method, 用 . 的會有 NoMethodError. 不過我建議可能可以考慮使用 __send__ 而非 send, 因為有些 class 的 send 有被 override, 一不小心可能會有未預期的錯誤。(雖然應該一看就知道會是出什麼問題就是了)

install.rb 則是在安裝時才會執行的程式,例如使用 script/plugin install OOO 就會先執行此 install.rb, 而日後不管何時使用此 plugin 時,install.rb 都不會再被執行。這部份可能會放的程式碼也許是會動到目錄結構的程式,例如在 app/models 下寫入 OOO_plugin.rb, 像這種只需要在安裝時執行一次即可。這樣做有什麼好處呢?我想感覺就有點像 script/generate OOO_plugin 的感覺吧。

至於 lib/ 則會自動加入 load path, 這部份跟 rubygems 裡 gemspec 裡的 s.require_path = 'lib' 是相同的概念。test/ 的部份也和 rubygems 相同。

接下來還有什麼好講的嗎?有的,參考〈acts_as_taggable Plugin 使用方式〉一文,可以發現 acts_as_taggable.rb 寫得相當複雜,這當然是有原因的。我參考了官方網站裡跟 plugin 有關的系列文章,發現 ActsAsOOO 是個相當常見的 plugin 模式,其中似乎有某種 pattern 在其中…。就是 ClassMethods, SingletonMethods, 與 InstanceMethods.

這樣做的原因很簡單,就只是加強模組化與避免名稱污染,所以多繞了幾個圈。在這種模式的做法下,ActiveRecord::Base 所多出來的 method 只有 acts_as_taggable. 而當你在寫 class MyModel < ActiveRecord::Base; acts_as_taggable; end 時,只有 MyModel 多出 class method => find_tagged_with, instance methods => tag_with, tag_list. 而不是整個 ActiveRecord::Base 都多出這些 methods.

其實這做法也有點像 meta-programming, 只不過是用 mixin 的形式。在前一陣子裡,由於我搞不太清楚 include 與 extend 的差異,所以調查了一下,並做了些筆記:(我知道多半連不上,所以 copy 一份到這)

=begin
2007.02.13

include 將 module 內的所有 method 原封不動拿過來,extend 則是在原本的 method 前面多加個 self. 即 class C 內,寫 include M 則追加 instance method. 如果寫 extend M 則追加 class method.

module 內也可以使用 include 與 extend, 結果同 class. include 為 instance level, extend 為 class level. 基本上這兩樣東西是很接近的。至於 module level 的 method, 則不受 include 也不受 extend 影響。其實跟 meta-programming 很像。

module M
def m; end # 只能由 include 或 extend 取用
def self.m; end # 只能用 M.m 來呼叫
end

class C
include C # 獲得 instance method m
extend C # 獲得 class method m
end
=end

除此之外,module 還有個特別的東西叫 module_function, 這東西我還沒仔細研究,但初步認識的結果是,他會造成指定的 method 同時定義 def m; end 與 def self.m; end 詳細的內容可以當成讀者練習,呵。(雖然其實可能就只是這樣而已)

回到 acts_as_taggable, 在 module Taggable 中唯一的 method 是:
def self.included(base)
base.extend(ClassMethods)
end
這個相信大家都很熟了,就是指當此 module 被 include 時會執行的 method, 有點 hook 的概念。這裡的 base 就是 includer, who include this module. 在 Taggable 中,當然就是 ActiveRecord::Base 了。extend 上面看到了,會促使該 class 擁有此 module 中所有的 method 成為 class method, 所以 ActiveRecord::Base 多了個 acts_as_taggable 為 class method. 如此一來,就能寫:
class MyModel < ActiveRecord::Base
acts_as_taggable
end

其實我覺得這樣實作有點過於複雜,我會比較喜歡這種形式:
class ActiveRecord::Base
def self.acts_as_taggable
# ...
end
end
這樣就一目了然了。缺點當然就是會比較難將此 method 丟給其他人用,例如假設哪天有了一個 class 是 PassiveRecord, 也想將 act_as_taggable 丟給他使用,可能就必須 copy & paste, 或是一些非常詭譎的方式,這邊不多提。

而在 acts_as_taggable 的最下方,呼叫了:
include ActiveRecord::Acts::Taggable::InstanceMethods
extend ActiveRecord::Acts::Taggable::SingletonMethods
的意思就非常明顯了,將 module InstanceMethods 的 method 變成 MyModel 的 instance method, 將 module SingletonMethods 的 method 變成 MyModel 的 class method.(不太明白為何要取做 SinglethonMethods, 也許只是因為 ClassMethods 用過了吧?)

原因是 :include 與 :extend 的 message receiver 是 MyModel 而不是 ActiveRecord::Base, 所以被擴充的是 MyModel; ActiveRecord::Base 不受影響。

大抵上就是這樣了。

abstract:
init.rb 在 Rails load up 後會執行
install.rb 在 plugin install 後會執行
lib/ 自動加入 load path

下次再研究 rake 要怎麼用,相信以之拿來做 test 會很方便。

2007.05.05 godfat 真常

延伸閱讀

12/27/2006

Yullio 試用感

Mollio 是一個我很喜歡的 CSS template,就連我自己架的中文 Ruby 星球也是用 Mollio。今天早上看到 HappyDesigner 發表了 Yullio: YUI CSS Grids 與 Mollio 的結合,看到裡面講
hlb根據YUI Grids與Mollio CSS/HTML Templates,製作了一套名為 “Yullio” 的模版(Yullio = YUI + Mollio)。
並且發現到,他們還有 Rails 的 Plugin,還可以Gem 安裝!!!所以二話不說就先裝了要緊。

安裝方式就是
gem i -y layout_yullio_generator
使用方式就是
ruby script/generate layout_yullio layout_name sidebar_name
假設如原網頁講的打 ruby script/generate layout_yullio Expense sidebar ,他會生成一個 layout 叫做 expense,然後在 app/view 裡面有一個 _sidebar 的 partial ,記載了 sidebar 應該放的東西。並且他會將所有需要的 CSS、JavaScript 及 plug-in 放一份到 public/ 及 vendor/plugins/ 目錄下。其他詳細文件請跟原作者聯絡

最上面那張圖是我使用這個 plugin 自動產生的頁面,還不錯吧,以後這個 plugin 對我來說應該會很常使用到吧,感謝 hlb 和 lukhnos。

版權宣告

Yullio Layout Generator 是使用 BSD 授權公開發表的軟體套件,由 hlb 和 lukhnos 製作。YUI Grids 及 Mollio 的授權,請參考其各自的網頁。


延伸閱讀