over 10 years ago

這一篇的重點是 Object-Oriented View。

14. Decorate using Decorator ( don’t put everything in model )

在前面我們介紹了幾個手法,包括 將 Logic 收納到 Helper 裡面

def render_article_publish_status(article)
  if article.published?
    "Published at #{article.published_at.strftime('%A, %B %e')}"
  else
    "Unpublished"
  end
end

以及 將 Helper 裡面的 Logic 重新整理到 Model

Read on →
 
over 10 years ago

這一篇的重點是除了 Helper 和 Partial 外,還有什麼工具是可以拿來用來整理 View 的。

11. content_for ( yield )

有些開發者不是很了解 Rails 的 yield 是拿來作什麼的。你可以理解成 跳躍到定點執行

這招常被用在一個情景上: Best Practices for Speeding Up Your Web Site 中的 put javascript at bottom。

在調整前端 performance 時,最常見也最有效的一招就是,把肥大的 JavaScript 放在最底端讀入執行,因為很多 JS 都是 document.ready 才會被執行。

但是,如果開發者只是把 javascript_include_tag 丟到 view 的底下,如:

Read on →
 
over 10 years ago

這一篇的重點是 Partial 的設計

7. Move code to Partial

什麼時候應該將把程式碼搬到 Partial 呢?

  • long template | code 超過兩頁請注意
  • highly duplicated | 內容高度重複
  • indepdenent blocks | 可獨立作為功能區塊

常見情境:

  • nav/user_info
  • nav/admin_menu
  • vendor_js/google_analytics
  • vendor_js/disqus_js
  • global/footer

8. Use presenter to clean the view ( 使用 Presenter 解決 logic in view 問題)

在前一章節,我們介紹過 view 裡面常被迫出現這種 code

Read on →
 
over 10 years ago

這篇是我在 RubyConfChina 2013 的 Talk:Maintainable Rails View

當初會整理這個 Talk 的原因是因為長久以來:相對於 View,在一個 Project 裡面,設計出乾淨的 Model 與 Controller,是相對簡單的。但事情一跑到 View,就會變得相當複雜。很難有一個基礎簡單的思路去整理這些糾結的線條。

所以我最後決定釋出一份這樣的整理指南。這其實也是我們 Rocodev 目前在用的 Rails View 整理技巧。

前情提要

要了解這些用法中間的轉折,首先我必須先解釋幾個前提,這是這些「整理方法」之所以被發明的原因:

  1. 在 View 裡面有 Logic 糾纏 ( if / else & other syntax )是不好的,這會導致 View 很難修改以及維護
  2. 在 View 裡面有 Logic 糾纏是不好的,這會導致 View Performance 下降 ( pure logic )。
  3. 在 View 裡面有 Logic 糾纏是不好的,這會導致 View Performance 嚴重下降 ( with data query )。而這包含在 Helper 裡面 perform data query。

這個 Talk 會包含以下幾個主題:

  • Helper Best Pratices
  • Partial Best Pratices
  • 除了 Helper 與 Partial 之外的整理武器
  • Object-Oriented View
Read on →
 
over 10 years ago

從這次打造 Logdown 的過程中,我還學到寶貴的另外一個經驗:「先做軟體,不要先做平台」。

打造「平台」,幾乎是大多數人對於做網站的第一個預設想法。

如果你跟你的朋友說你要做一個網站,然後說你不是做平台,那麼大概有 99% 的機率他會以為你是瘋子,不然就是超級外行的笨蛋。如果你跟投資人說,我們沒有要做平台。嗯,他可能連見你都懶....XDD

盲點一:「平台」=「賺錢」=「成本很低」

這個道理很簡單:「平台」=「很大」、「大」=「賺錢」。「平台」=「很多人會主動貢獻內容」、「很多人會主動貢獻內容」 = 「成本很低」。所以,如果你一開始就宣告不做平台,「正常人」會把你當「瘋子」。

盲點二:一開始就要作「功能完善」的「平台」,不然之後修改的成本很高

因為從以前到現在,打造一個網站因為架構和技術上的關係。軟體修改的成本一直被認為是很高的,所以絕大多數人的傾向是:「我應該一開始就要企劃完善,否則之後修改的成本很高。」

而網站上線之後,若網站一開始使用成效不佳,大多數的人檢討也會傾向「應該是宣傳不夠的關係」。


網站失敗的例子很多。後面的結局在趨勢部落格上多到不勝枚數。但有幾個共通點幾乎都是一模一樣的。

  1. 平台「大」不一定「賺錢」。「大」但不知道如何「賺錢」、虧得一屁股的到處都是。
  2. 作平台不等於「很多人會主動貢獻免費內容」。事實上一開站就是死城的平台到處都是,而這件事情會嚇到很多第一次的網站經營者,所以他們下意識的直覺就是花錢「請人來生內容」以避免「死城效應」,否則「死城效應」會讓這個網站在第一個月直接就 GG。
  3. 幸運的用錢熬過頭幾個月後,發現使用者和內容成長的速度還是遠低於預期,沒有使用人力或資本介入,直接放著還是會 GG...

.....(後面的事情寫都寫不完...)

但總而言之,「規劃」過的網站上線後多數的問題是這樣的:

(1) 事情不會如想像一樣的發生。而且絕對比你當初預期之外的的最糟狀況還要更糟。
(2) 不知道如何解決「死城效應」,只好用錢續命。
(3) 六個月以後終於發現問題完全不在「宣傳不夠」。而是在於「沒有解決任何問題」、「解決的問題價值不夠付薪水」。
(4) 九個月以後,發現解決根本的問題的手段在於「改版」(因為原先的手段錯了)。但「改版」會遇到的問題在於「舊的功能是否要留著,砍了很可惜」。「新舊版本並存的軟體成本問題」。「開發新版本的金錢成本問題和時間成本問題」。


承認失敗永遠是走出困境的第一步 : 往反方向而行

無疑的這對誰來說都是一個很難解決的困境。勵志書上常說「承認失敗永遠是走出困境的第一步」。我在自己上班的時候,曾經作過很多網站。在做過那麼多網站之後,我認知到,「先作平台」是一條我完全負擔不起的路;上班的時候公司的錢多到花不完了,但是還是避免不了掉入這樣的問題。現在我擁有的只是一個更小的公司,自然更玩不起這樣的作法。

所以我這次實驗方向就是:反方向而行。不要急,只做我做得到的事情,看看什麼事情會發生 ( 原先的公司 Rocodev 主要業務還是接案作軟體開發,內部生個產品還算養的起)。令人驚訝的是,我看到了許多以往我從來沒有想像過的世界。

「第一時間」不作平台,只「解決問題」

雖然打造一個「像樣」的「平台首頁」對我們來說「開發成本」是很低(註)的。而且在產品上線之後,許多朋友就覺得我們有機會打造像 Medium 這樣的寫作平台。一直希望我們加上「社群首頁」、「社群功能」,往平台發展。

但是我們一直有意無意的「拒絕」這麼做,直到最近才終於上了 Explore Logdown 這個功能。

在此之前,我們幾乎把接近 100% 的重心都放在打造 Logdown 的「寫作工具」之上。我們花了非常非常多的心力在打造這個編輯器之上,如果你知道我們在這個 Markdown 編輯器上花的心力、開發的 Editor Hack、和遇到的問題難度,你可能會嚇一大跳...(有空我會請同事簡單介紹我們解決了哪一些在實作編輯器上的 issue ...)

因為我相信一件事:「有解決到的問題的軟體才會有人要用。」

身為平台者開發的我們,往往會有一個巨大盲點:「我開發了一個平台,就會有人主動來用。」但若以使用者的角度來說:「你開發了一個平台,關我屁事,為什麼我要主動用,甚至從此搬到你家。」

我們之所以會把所有的優先權,幾乎全部貫注在後台功能之上。是因為如果連自己都無法愛上我們自己的產品,不覺得不用 Logdown 寫部落格會死,那怎麼有可能讓其他人也喜歡用。

而一旦大家喜歡用這個工具寫文章,自然內容的產生質和量就不是什麼問題了,作平台的門檻也沒有那麼高了。

(目前文章不包含搬家的文章,在 Logdown 上大概每個月文章產生速度大約在 6000 篇以上,系統文章數量目前是 14 萬篇以上...)

只有一件事需要作:「把產品做好」

Logdown 在頭三個月幾乎不作所有「社群平台」有關功能,還有一個沒什麼人知道的好處:不用花時間去分辨使用率低到底是因為宣傳不夠的關係,還是純粹只是因為產品爛...。如果沒人用,就是產品很爛沒有第二句話好說 XD

如果一個產品的問題相對只剩下產品品質,那需要努力的方向也只剩下「把產品做到好」。不用為其他不存在的事情忙的焦頭爛額還一點效益都沒有。

軟體就是必須要一直調整,一直砍掉重練...

在做這個產品時,我們還在心理上先接受了一個事實:作產品就是要一直修改一直修改一直修改,必要時還要砍掉重練。雖然我們是職業軟體開發團隊,但不代表我們就沒有開發成本問題。

修改在往常讓 RD 難以接受的是因為:

(1) 軟體修改成本很高。(有可能因為是當初假設錯誤,或者是程式碼規劃不好)
(2) 你不知道修改之後,效果會比之前的好還是之前的爛。情感上很難接受以前的功能是「作錯了」這件事。或者只是「不覺得別人(通常是 PM)的意見是正確的」。

因為我們的開發是 Complain Driven Development(被不同人抱怨三次以上我們才要作)。Feature 正確率大概有 90% 以上...

當團隊基本上知道自己起碼是按照一個「相對正確」的方向走過去。心理上就不太會抗拒「修改所產生的成本」這件事。

一開始就作「收錢」的產品

許多人會對 Logdown 的「大膽」感到驚訝,為何一個「開發中」的軟體,就打算收錢?這在許多方面看來都是覺得奇怪的:

(1) 你們不是第一個作部落格軟體的。如何有自信讓使用者「好」到「值得付錢」?
(2) 一開始就收錢,不會把使用者「嚇跑」嗎?

我想這沒有絕對的答案。若要談當初的想法,我的觀念純粹是:

(1) 我現在開公司,做的東西理論是就是要收錢的,只是何時才要收?
(2) 我不想按照免費使用者的建議努力去作,做出一個到最後叫好(很多人稱讚)不叫座(根本不賺錢)的東西。
(3) 我不覺得「好」= 「值得付錢」。我認為「有解決問題」= 「值得付錢」。

我想最快知道要如何賺錢的方式,就是用直接一點的手段去作,直接收一個「還可以接受」的價錢,看看會發生什麼事。當然這個手段,現在由結果看算是相當不錯的:

(1) 網路上馬上有人罵,「哇靠,一個 Beta 的東西,沒有 OO 和 XX 還敢收錢,要不要臉」。我馬上就知道原來要收錢,至少要作 OO 和 XX,不用猜老半天...
(2) 朋友密我:「hmmm...但是你們可不可以把哪個 feature 改成這樣那樣,只差這個,我就願意付錢了。」我馬上就知道,哦,原來某些使用者最在意的是什麼功能一定要做到,他們願意付錢就只買這個功能。
(3) 其他公司直接打給我說:「hmmm...你們可不可以作什麼什麼功能,我願意付錢作 Enterprise 客製。」

我們是一個非常小的團隊,根本沒有能力負擔市場調查的成本,但是只公告了我們要收錢,我們馬上就知道要如何賺錢了。而甚至結果也不差,我們第一個月的收到的數字甚至可以打平整個開發團隊的薪水...(原本以為作部落格一定會賠的要死...)

甚至還有更意料不到的事情:使用者說我們的編輯器只用在部落格系統上實在太浪費了,可不可以作什麼什麼系統也用這個編輯器,他們也願意付錢 XDDDD

Summary

當然,這樣做的心臟要非常大顆。因為這樣做的方式,跟「一般作網站」的方式,差異非常非常的大,遭受周遭的人質疑也會非常非的兇,你甚至不可能在一開始拿到任何資源,因為沒有人會相信你這樣會是有結果的。但不可諱言的,這卻是我一直想做的一件事,我一直想看看這樣作,最後究竟會看到怎麼樣的世界。

我在 2011 年寫過一篇文章: 魔球,一個搞清楚產業重點的棒球故事 。這篇文章其實就是當初我創業的動機之一,只是在這篇文章裡面這樣的手段,不管身在哪一個公司,是不可能被任何老闆准許的。所以最後才走上了創業這條路。

Logdown 才剛開始沒幾個月,現在小小的成績實在不能證明什麼。但是,我開始知道原來不走那條相對很遠的路,也是可以的。


廣告:歡迎試用 Logdown,一個很棒的部落格寫作平台,我猜你應該也會喜歡它的。

 
over 10 years ago

Lean Startup 是最近幾年來的顯學,其主要的訴求是創業者應該先把重點放在打造一個 Minimum Viable Product (MVP),觀察市場的需求,再行調整變化商業主軸成長。

這一套理論在近年來被大家一直傳誦,已經讓人聽到長繭了。但是最讓眾人疑惑的其實還是幾點:

(1) 所謂的 Minimum 到底要砍到多少才是 Minimum ?
(2) 這麼少的 Feature 的站台,難道真的會有人來用嗎?
(3) 如何證明一開始少功能的站台真的比多功能的站台好處還要多?
(4) 開發一個產品,初期應該做到多少功能,才算是足夠?

這幾個問題,也是我一路以來一直想知道的答案。

我們團隊最近很紅的產品 Logdown,其實從來不是一個「計畫中」的產品。它只是一個我們眾多一直很想做的點子的其中一個,然後在 Hackathon 中莫名其妙的被做完了原始的 prototype,然後莫名其妙的變成了產品,最後莫名其妙的變成了一間公司。

從這三個月莫名其妙的旅程中,在 Logdown 的茁壯過程,我趁機會執行了一狗票過去幾年來一直我觀察到的現象、卻從來沒有機會在職場上印證的假設,意外的得到了不少答案...

多小的產品規格才是 MVP:"一個"足夠解決問題的功能範圍

在做 Logdown 之前,我自己作過很多產品,也幫客戶作過很多專案。讓我一直抱有疑惑的是:以規格數來算,在規格裡面被規劃幾條是 MVP?如果以一個中型公司「產品企劃」企劃出的專案,通常規格會多達 20-30 項,那麼所謂的 MVP 是不是 10 項?還是 7 項?

而我後來發現,其實嚴格的定義來說,只需要「一項」。市場上需要一個夠好的寫作平台,嚴格來說,是需要一個夠好的網頁文字編輯器,其實作這樣完全就夠了。

當然,我們當時沒有這麼大膽,覺得作這一個平台只需要這個功能。但是我們的時間和精力只夠我們當下只做這一個功能,然後在這個給定的時間內,做到最好。(因為是在一個 Hackathon 中做的..)

在上線以後,我們就發現這個功能就足夠吸引幾百個人對我們的東西有興趣...

這個原始方向「也許」是對的,所以才會有後續的開發維護。

不是減法原則:直接讓用戶告訴我們什麼是最重要的

把一個只有一個功能的原型變成一個產品,後續的最大挑戰是你知道既有市面上類似的產品有哪些功能。但是,一個成熟的部落格平台有 100 項功能,我們到底要先實作哪些功能?

有些人可能會建議,把 100 條功能先整理調列出來,賦予權重,再投票篩出作最重要的 10 個功能?但幸運的是,因為當時我們公司實在是太忙了,沒有美國時間作「整理」這種事。

我們實作的功能順序是:只要這個功能被不同的 user 抱怨超過 3 次(這種基本功能你們竟然沒有??),這個功能才會真的進我們的 issue tracking list,稱之為 Complain Driven Development。

我們得以把最值得投資的功能在很早的階段就都做完,不用花時間在做錯的功能,再陷入到底要砍還是繼續維護的兩難。

當然,上述的這兩個作法,表面上的風險很大,在一般的公司我的老闆絕對不允許我幹這種事,好險這間公司我是老闆(笑)。

產品 V.S 裝置。既然是作產品,就需要包裝。包裝會帶給產品超乎想像的改進作用...

Faria 的老闆 Theo,曾經寫過一篇文章 Building Complete Products vs. Devices,我相當認同他的觀點。

他提到一個「產品」,內容應該具有以下必備:

  • Support
  • Blog & Twitter
  • Demo Video
  • Setup & Getting Started
  • Print Materials
  • Behind the scenes

一直以來,我覺得台灣大部分的網站都是屬於他口中的「Device」,使用者進來一個網站:

  • 開發者把所有能呈現的功能一口氣呈現
  • 找不到自助說明文件
  • 找不到官方交流管道
  • 找不到回報問題的地方

這也是我之前作產品時的毛病。只是在當時,我並沒有意識到這是 "Product" V.S "Device" 的問題。

也一直以為這些事情是 Beta 站的常態:

(1) 試用的朋友在玩過我寫的 prototype,會開口問:「嗯...你這個應該是 XXX 服務吧?我剛花了一陣時間才搞懂這是什麼」
(2) 使用者只有微小建議,但找不到「只做建議」的地方
(3) 使用者只有卡在小地方,但找不到「只想偷偷問」的地方
(4) 使用者不知道這個服務最近上了什麼新功能,找不到官方素材可以幫忙寫文章廣告...

開發者無法第一時間有效傳達這個服務想解決什麼,使用者也沒有順暢的管道可以反應有價值的意見給開發者。但這些問題似乎卻沒有一些有效的方式可以一次解決。

產品包裝的實驗

而這次作 Logdown,在頭兩週快馬加鞭把大多數的重要功能寫完之後,我決定一反常態,把所有功能開發都暫停下來。讓同事們花了整整兩週,接下來只很奢侈的專注在做首頁與 Tour。

我一直想知道為什麼歐美的網站要花那麼多時間作 Landing 頁面以及 Tour 頁面,Fouder 還那麼有空寄信問候每個使用者網站用得爽不爽?我只知道也許作這些事也許可以解決上述遇到的那些問題...

後來的事情發展證明這個早期投資完全是值得的。我們得到了遠超乎預期的效果:

不需要重複的一再介紹自己

Landing 與 Tour 讓我們不需要重複的一再介紹自己(而且是對世界用戶),使用者幾秒內就能決定這是不是他需要的服務。而 由引導式的流程導引陌生的使用者註冊。

重新審視「產品的核心賣點」

而在製作 Landing 和 Tour 的過程中,也逼開發者重新審視「產品的核心賣點」到底是什麼。

  • 寫英文 Tour 真的很累,所以根本不想要作太多雜魚 Feature,以免還要寫介紹...
  • 在截圖時,發現當初 UI 做的不夠精緻,根本不能上相,只好 UI 大調整...
  • 部落格系統的某些核心重要賣點,我們根本還沒實作。只好趕緊調整優先權,趕快做出來,以免沒有內容可以放...

如果不是採 MVP 的成長方式,我們可能連 Tour 都寫不出來,也沒有機會邊做邊「精練」。

重新審視使用流程是不是合理

我們在寫站內的教學時和對照用戶的來信時,也發現使用者操作功能的流程完全不是我們預期的那樣。如果沒有撰寫 Help,我們不會知道我們設計出的操作模式有些其實完全不合理...

寫信,大量的寫信。使用者絕對主動會告訴你許多開發者想像不到的秘密...

最後一件事我學到的就是:寫信,大量的寫信給你的 user。我平常也是很多 SaaS 用戶的使用者,之前我常覺得一些服務的 Founder 很假掰,怎麼有空寄給使用者問候信。難道是生意太冷清絕望了,才問使用者哪裡做得不好?

收到這些信,偶爾我心情很好也是會回幾封,跟他講我覺得哪些功能不合理,為什麼我用完之後還是沒太高意願付錢。

但是我其實從來沒想過要抄這一招。因為我真的不知道寄信給使用者打招呼要幹嘛....(矛盾?)

但是這次不知道哪裡來的好奇心,想說順便在 Logdown 裡面寄信給 user say hello(設定在註冊後三小時後),想看看會發生什麼事。結果竟然打開潘朵拉的盒子....

我竟然遭受到了有生以來最恐怖的客服信 DDoS,在 Logdown 開始寄 hello 的信的第一個月,我收到了幾百封的使用者意見信,回到手真的是差一點都快斷掉了。

這才讓我意識到,不是使用者小氣不肯給開發者意見,他們只是懶得上 support,或覺得事情沒嚴重到值得寫 support。但如果是你「順便」寫信問候他,他會很樂意「順便」「回信」「給你一點意見」。

我們從這些幾百封的信裡面得到了大量的改進意見,許多的點是我們從沒想像到的。而且與我們官方 Support 上的 issue 類型都完全不一樣。

如果是像以前 device 型的作網站。我們不知道會多走多少冤枉路...

Summary

Logdown 是我們團隊作過有史以來「最少」「功能」的一個網站。最少功能並不是指實際上被開發的功能最少,而是經過「正規」「規劃」所做出的功能最少。幾乎都是「被很多使用者要求」「真的有必要」才作。

但是就要到達一個「Product」的標準,雖然一直走在「應該是相對正確的方向」,但實際上真的付出的力量遠超過作那些正規的 "Device" 網站。

所以真的不需要再擔心 Minimum Viable Product,Feature 太 Minimum 的問題,打造 Product 與 Device 是需要耗費完全不同等級的心力的...。

 
over 10 years ago

基本用法

不需要傳參數的狀況

不需要傳參數進 cells

<%= render_cell :statics, :today %>
class StaticsCell < Cell::Rails
  def today
    ....
    render
  end
end
  • 如果不需要參數,那麼就跟一般 ruby method 一樣
注意
  • 如果這個 method 需要 render view ( 90% 的狀況需要),method 最後一定要加 render。很多開發者 debug 到死就是因為不知道要加 render

  • 換句話說,如果沒加 render,其實可以拿來吐 cached 過的 pure result,如 ArrayString...etc.

需要傳參數的狀況

在上一篇文章,我們需要傳一個 user 進去撈資料。那麼 View 是這樣寫的

app/views/users/show.html.erb
   <%= render_cell :user, :rencent_posts, :user => :user %>

如果需要傳多個以上的參數,後面繼續加就可以了。

   <%= render_cell :auction, :daily_data, :user => :user, :date => Date.today %>

在 Cells 裡面收資料,使用第一個 argument 收進來。

class AuctionCell < Cell::Rails
  def daily_data(args)
    @user = args[:user]
    @date = args[:date]
    render
  end
end

打 Cache

在一般的狀況下,使用 Cells 是「沒有 cache」的,也就是普通的 View component 而已。要有 cache 效果必須特別指定...

指定時間

class UserCell < Cell::Rails

 cache :recent_posts, :expires_in => 1.hours

  def recent_posts(args)
    ...
  end
  
end

指定條件 cache

有時候,開發者希望該個元件除了一個小時 Cache 之外,還希望這個物品一旦內容被更新時,不要管一個小時原則,立刻更新。那麼可以用這樣的方式更新。

class ProductCell < Cell::Rails
  cache :product, :expires_in => 1.hours do |cell, item|
    "#{item[:product].id}-#{item[:product].updated_at}"
  end
  
  def info(args)
    @product = args[:product]
    render
  end
end  

使用 Helper

在 cells 時,是無法用 Rails 裡面的 helper 的,所以會出現 method undefined,必須在 cells 手動宣告。

class ProductCell < Cell::Rails

  helper ProductsHelper
 
  def info(args)
    @product = args[:product]
    render
  end
end  

使用 Cells 外的 partial

在 Cells 時,也是無法用 Rails 內的 partial,必須手定指定

class ProductCell < Cell::Rails
  append_view_path("app/views")
 
  def info(args)
    @product = args[:product]
    render
  end
end  

使用 current_user

有時候想偷懶,在 Cells 時直接呼叫 devise 裡面提供的 current_user,而非傳一個 user 參數進來。(我很不建議直接使用 current_user ...)

不過這也是可以做到的。在 Cells 裡面宣告 helper_method 就可以了。

class UserCell < Cell::Rails
  include Devise::Controllers::Helpers
  helper_method :current_user
   
  def recent_posts(args)
    ...
  end
end


這篇就大概 cover 使用 Cells 95% 上會遇到的狀況了。

後面幾個用法是 undocumentd features,解法當初是在 issue list 上或是 source code 撈到的...

小結

雖然現在已經有 Cache Digest 了,但我本身還是偏好使用 cells。理由當然是不外乎 cells 還是可以解決不少封裝的問題,而不只是當作 cachable partial ....

如果你在專案中常遇到 View 有複雜難以整理的問題,我相當推薦使用 cells

另外作者 Nick,在 Twitter 上有帳號 apotonick,歡迎 follow 他。

兩年前 (2011) 他也有來台灣參加過 RubyConf Taiwan,當時是借住在我租的公寓....XD。

系列連結:

 
over 10 years ago

適合使用 Cells 使用的場景

例如在設計 User Profile 頁面的時候,我們會遇到規格裡面希望有這樣的設計:

  • 顯示這個使用者最近的文章五篇
  • 顯示這個使用者喜歡過的文章五篇
  • 顯示這個使用者近期的五篇留言
  • 設計上點擊時「不切換分頁」,也「不 Ajax」,點下去馬上要能看到。

在 controller 裡的 code 會是這樣的

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    @recent_posts = @user.recent_posts.limit(5)
    @favorite_posts = @user.favorite_posts.limit(5)
    @recent_comments = @user.comments.limit(5)
  end
end

而在 View 裡面的 code 會是這樣的

app/views/users/show.html.erb
   <%= render :partial => "users/recent_post", :collection => @recent_posts %>
   <%= render :partial => "users/favorite_post", :collection => @favorite_posts %>
   <%= render :partial => "users/recent_comment", :collection => @recent_comments %>

衍生的問題

實作這個頁面時,開發者會遇到一些衍生的 issue。這個頁面一次要撈太多資料了,所以需要上 Cache。那麼 Cache 的標準是依照什麼呢?是針對 User 的 Update At 嗎?

好像不太有效率。假如我希望 Recent Post 與 Recent Comments 被 Cache 的時間長短不同呢?

......

光用想像的,你應該可以想像這個 action 和 view 的程式碼馬上就會被污染到不堪入目...

用 Cells 改寫

如果用 Cells 改寫,那麼程式碼會是這樣的。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end
end

app/cells/user_cell.rb
class UserCell < Cell::Rails
  def recent_posts(args)
    @user = args[:user]
    @recent_posts = @user.recent_posts.limit(5)
    render
  end
  
  def favorite_posts(args)
    @user = args[:user]
    @favorite_posts = @user.favorite_posts.limit(5)
    render 
  end
  
  def recent_comments(args)
    @user = args[:user]
    @recent_comments = @user.comments.limit(5)
    render
  end
end

原先的 View 改成

app/views/users/show.html.erb
   <%= render_cell :user, :rencent_posts, :user => :user %>
   <%= render_cell :user, :favorite_posts, :user => :user %>
   <%= render_cell :user, :recent_comments, :user => :user %>

看起來好像沒什麼差別? well,你可以這麼做:

app/cells/user_cell.rb
class UserCell < Cell::Rails

 cache :recent_posts, :expires_in => 1.hours
 cache :favorite_posts, :expires_in => 3.hours
 cache :recent_comments, :expires_in => 5.hours
 
  def recent_posts(args)
    ...
  end
  def favorite_posts(args) 
    ...
  end
  def recent_comments(args)
    ...
  end
  
end

馬上就可以把 Cache 和邏輯封裝做的非常漂亮。


在下一篇,我們會介紹 Cells 裡面常用的 API 與一些奇技淫巧。(你會幾乎不知道可以這樣用...因為文件上沒寫...)

系列連結:

 
over 10 years ago

之前寫的 Cancan 實作角色權限設計的最佳實踐OmniAuth - 實作多方認證的最佳實踐 系列剖析頗受好評。

社群朋友希望我再寫幾個常見但原理不是那麼好懂的 Gem,這次的主題是 cells

Cells 的 Github 首頁的 description 是 View Components for Rails。在 DHH 發明的 Caches Digest 推出前,它應該是開發者最常拿來處理 Partial Cache 的 Gem。

Model – View – Presenter (P.S. Don't do this)

在真正進入主題談 cells 時,我們先來談談 MVP 這個設計手法。MVP 是 Model – View – Presenter 的縮寫。

在一般實作 MVC 架構時,我們強調的是:

  • 用於封裝與 「業務邏輯相關的資料」 以及 「對資料的處理方法」 必須放在 Model
  • 至於 View ,只負責 「負責資料與介面的呈現」

不過,這是理想狀態。

專案中實際上會發生的狀況是:

  • 網站 UI「高度依賴業務邏輯」
  • 業務非常複雜,有時會逼得開發者不得不在 controller 或 view 中實作業務邏輯
  • 雖然實務上會教我們將複雜的 method refactor 到 model 中。但現實狀況是:有些業務用 method 僅在該 View 使用一次。而且不屬於 Model 應有的基礎 method。什麼都往 model 扔的化,model 會變得異常肥大。

MVC 這樣的架構最後會不敷使用。最後會需要再實作一層 Presenter 封裝

  • 將比較少使用,但又必須實作的複雜 / expensive 業務邏輯,抽出來放在 Presenter
  • 一旦業務邏輯抽出來放在 Presenter,改動 UI 的難度就會變得比較低了
class Sites::ShowPresenter

  def hottest_topics
    @hottest_topics = @site.topics.hottest.limit(10)
  end

  def recent_topics
    @recent_topics = @site.topics.unhidden.recent(10)
  end

end
def show
  @presenter = Sites::ShowPresenter.new(@site)
  @headline_topic = @presenter.headline_topic
  @categories =  @presenter.categories
  @site_alias = @presenter.site_alias

  add_breadcrumb @site.name, site_path(@site)
  seo_meta_desc_keywords(@site)
end

Cells ( View Components for Rails )

cells 的作者 Nick 曾經寫過一篇文章 Rails Misapprehensions: What the fuck is MVP? 抱怨過 MVP 這個設計手法在 Rails 並沒有解決「真正的問題」。而這也是他設計 cells 的動機。

Presenter 只處理了 complex view logic 的部分。

但我們真正在專案中會遇到的狀況是,專案裡

  • View 裡面太多邏輯
  • 因為 View 裡面太多邏輯,會有 Performance Issue。所以會進行 Cache。但 Cache 了以後很難寫測試..
  • 根本沒辦法對這個 action 寫測試,因為太複雜了 ....

Cells 的想法是開發者並不需要強行在一層的 MVC 架構裡,在 Controller 把一次事情做完(撈資料,處理資料)。開發者可以使用 Cells 把這些複雜邏輯拆成一個一個獨立的邏輯元件(componet)去實作。而這些 component 是可以被複用、被 Cahce、可以被測試的。

簡而言之,在用 Cells 時,你是可以把 Cells 想成這樣的:

  • 可以複用可以 Cache 的 Partial
  • 這個可以被 Cache 的 Partial,自己有一個迷你的 Controller 和 View


在下一篇,我們會介紹 Cells 適用的場景與用法。

系列連結:

 
over 10 years ago

Hi, 各位

Logdown 自即日起提供付費會員 (Preimum) 服務。售價 49.99 USD/year。

為了感謝各位早期使用者的支持,我們在 2013/9/13 前提供 付費會員限時半價 50% 折扣,也就是你可以以 24.95 USD/year 的價格購買會員資格,一次最多可以買 3 年。

折扣啟用方式是登入 Logdown 以後,至 http://logdown.com/account/settings/plan 付費會員訂閱畫面,先「升級」至 Beta 功能,再「升級」 Preimum。這樣你就能使用以 24.95 USD 價格升級的優惠。一次最多可以買 3 年。

我們將會在 9/13 日移除「升級至 Beta」的這個選項。所以手腳要快!:D


P.S. 優惠只有一次,請謹慎使用。假設如果你先購買了一年 24.95 的會員。接下來還想再買兩年,升級選項只會剩 49.99 的價格。

P.S. 我們會在 2014 年調漲到 72 USD/year

P.S. 我們已經有 Custom Theme 的服務,詳情請見這份公告 Lgodwn 支援Custom Theme 了!。另外前系統裡面提供了四個官方預載 Theme,明天會有第五個。