丹哥的技術培養皿

A blogging framework for hackers.

使用 Redmine 來建立個人的長期計畫

| Comments

為了要可以嘗試使用 redmine 來建立個人計畫,我們得先安裝好 redmine , 我在網路上看到了這篇文章,一步一步做的確可以成功地建立在 Heroku 上喔。 如何在Heroku上架設Rdemine服務,使用S3儲存附件

另外也可以使用由德國公司開發的 雲端 SaaS Redmine 服務 : plan.io 網站: plan.io 有提供免費的方案給個人使用,缺點就是唯一這個免費的 Project 會被設定為 Public ,而且不可改為 Private, 註冊的地方在 Pricing 的頁面,請搜尋 Bronze 這個字, 或點這個連結也可以導過去註冊免費方案 => https://accounts.plan.io/signup/Bronze?locale=en

另外在網路上搜尋著 Redmine 之於個人的使用模式及 know how ,發現了下面這篇,因此很快地把他先翻譯成中文讓大家參考。 原文文章 個人的な Redmine で長期計画を立てる

Life Hack

如果是置身在軟體開發的世界當中,在公司內使用 Redmine 的人 (或是被強迫使用的)是不少的。使用這個軟體來管理個人的專案,這是非常有用的。

參考這篇文章 意外と使われていない「個人用trac」活用のすすめ | ランサーズ(Lancers)社長日記 翻譯後的中文文章在這 : 推薦您所忽略的出乎意料地沒在使用的[個人用 trac ] 活用術

好的地方

1.可以任意的將票(Ticket)分類 Ticket 就是 Task 或 Job ,Ticket 的 Tracker , 優先順序 , 任意的分類 , 任意的 item 屬性等等,我們都可以自由的設定與分類。 而且,如果使用了客製化的功能,都可以透過自訂的分類或排序來針對現在的狀態一目了然。但是,在高端的工作(Task)管理軟體例如 Omnifocus ,這也是辦得到的。

2.建立長期計畫 這就是把 Redmine 拿來作為個人使用最大的好處了 。 目前現況用來作為TASK管理的工具及軟體,再怎麼樣最多也是只能拿來建立一年兩年左右的長期計劃。 在使用這樣的軟體時,有一些活動,例如 : 「清理冷氣機」、「為了要參加Seminar使用,要製作個人名片」等等這種粒度比較小的專案 (Project) ,使用這樣的工具是便利的。 但是,如果要統合一堆小的專案(or TASK) 變成一個大的計畫或大專案時,例如,要開一場演奏大提琴表演的演奏會、獨立創業,等大計畫,如果可以有 【版本控制( Version ) 】 和【甘特圖 (Gannt Chart)】的概念的話,那麼,使用 Redmine 是會相當便利的。

3.也可以使用 ネタ帳 (還沒查到什麼意思) 雖然在意外と使われていない「個人用trac」活用のすすめ | ランサーズ(Lancers)社長日記 這篇文章裡面也已經介紹過了,ネタ帳是相當便利的東西。 使用 Evernote 的方式有很多種,但是只有使用 Tag 和 Notebook 的分類方法,此外,各種不同的idea 的種類要群組起來 (Grouping 起來 ) ,然後要一目了然的依據群族來看是不行的。 因此,使用 Redmine ,把 idea 化作 Ticket 的時候,就可以快速便利的依照分類群組來瀏覽,這樣是非常愉悅的。 當然,每一個各別的 idea 的詳細說明,自然而然就是到 Ticket 的詳細說明欄位去撰寫輸入。 對個人來說, 其次可以用來記錄 idea list 的地方就有 : 手機 App 的開發 List , Blog 的 List

不好的地方

  1. 建置 redmine 的程序很麻煩 不論是 IT 工程師,或是不屬於 IT工程師的人,要安裝 Redmine 都是有一點麻煩的。 因為可能為了要達到不論都在哪裡都可以存取這些資料, 因此就考量到必須要有 web server 的建置,這樣的功夫就必須耗費的更大了,造成更多的困難。

個人的使用方法

ticket = mini project 切票的粒度大小是很困難的。 舉例來說,「買一台新的洗衣機來更換」這樣的一個 project ,這是可以再分解為一些 task的。 例如: 「測量現在使用的洗衣機尺寸」這樣的一個 task 是不是要在開一張票呢? 在自己使用redmine管理工作時,「買一台新的洗衣機來更換」是不是就只會開一張票而已呢? 如果這件工作「買一台新的洗衣機來更換」,要實際完成的期限是在近期的話,那麼,這件工作就會被移到 Omnifoucs ,然後在 Omnifoucs 上面列出詳細的子項工作。 具體的行動方案的 task list 的管理,我感覺應該是要使用例如 Omnifouct 等等的 task management tool , project 管理 tool .

以月為單位的票管理

再者,不論是這裡,或是意外と使われていない「個人用trac」活用のすすめ | ランサーズ(Lancers)社長日記 這篇文章裡都有介紹了,以月為長度的票管理是比較好的。 【版本】用 “月別” 切換,在月的長度下開票,例如,分別設定了 2014年1月、2014年2月、2014年3月的不同版本,分別把票分配(指派)到各月份去。 此外,2014年4月以後的專案計畫,就放置在 2014年的版本當中。 2015年的專案計畫,就放置在2015年版本當中。 最近的 Task 依照細部的期限去分類,屬於將來的 Task 就使用大的單位去分別分類即可。

給新畢業的軟體工程師的技術書籍100冊LIST

| Comments

本篇原文來自於日本知名食譜網站 cookpad 的部落格: 新卒ソフトウェアエンジニアのための技術書100冊 http://techlife.cookpad.com/entry/2015/03/31/093000 以下為粗略的大意上的翻譯,提供給自己及朋友閱讀。 應該會有一些小錯誤,敬請見諒。


春天阿,就是新鮮人的季節。 這個部落格的讀者群當中的你們,明天開始也是會有要以工程師為職業踏進社會的人吧? 我們COOKPAD也準備好了要迎接新的夥伴們了,準備工作的其中一部分呢,我們製作了「給新畢業的軟體工程師技術書100冊LIST」。

這100冊,是給那些以職業軟體工程師作為職業發展的累積為方向的人,若你們苦惱於應該讀哪些書的話,我們做了一個讓你們大家首先可以從這個LIST裡面去選擇的提案。

在這個LIST裡面,一些是依照興趣、一些是依照我自己的主意來放進去的,雖然我相信你們會安心和氣地接受這份清單,然而還是要提一下,我是用一種認為身為一個職業的Programmer應該要知道哪些知識的心情來蒐集這個LIST的。

我只會取那些被稱為經典的書籍、然後會避免那些獨立學習時難以理解的電腦書籍。 雖然有許多都是被稱作必讀的書,但是有可能有一些書都已經絕版或者是有難以入手的情況存在的話,這樣這些書我也不會放到LIST裡面來。 (我是真的很希望你們能讀這些書的,因此) 我也會避免列一些英文(外文)的書籍。

而且,為了方便查閱,我也做了分類和難易度的區分。 沒有星星的標記的項目,則是那些就算不是以作為軟體工程師為目標的人也都是會看得懂的書。 一顆星的呢,則是入門者可以看得懂的書,隨著星星數量的增加,表示這些書的難度就越趨困難。

這100冊書,都是相當值得一讀的。 雖然如此阿,但是隨著經驗的不同,有些書可能也是會有看不懂、無法理解的時候。我想,我們所不理解的事情、不知道的事情被寫在書裏面這當然是理所當然的阿。

有一個我們社內很尊敬的軟體工程師前輩,ただただしさん也是在 「電腦的名著,古典100冊改訂新板」這本書裡面的一篇文章提到,讀技術的書,一定會有 “ 阿~~怎麼都看不懂 ” 這樣的經驗。 然後,關於會讓你產生這種感覺的這種書,是因為書裡面的內容是那種 「只讀一次是會讀不懂的書,先放下一會兒,然後再回來讀一讀,之後他能產生的價值可是會以數倍的回報」的內容阿!!

軟體工程師的工作是很困難的,在很多時候,我們必須要具備各式各樣的知識去應對問題。 因此,請務必讓這100冊的書能夠陪伴你成功的職涯之路。

(以下是各分類的日文-中文對照, 幫助大家看自己有興趣的分類)

アルゴリズム : algorithm 演算法 エッセイ: essay オブジェクト指向 : object oriented : 物件導向 サービス開発 : Service 開發 セキュリティ: Security 安全 ソフトウェア開発: Software 開發 データベース : Database デザイン : Design テスト : Test 測試 ネットワーク : Network バージョン管理 : Version 管理 , 版本管理 プログラミング : Programming プロジェクト : Project 專案 関数プログラミング : Funtional Programming 機械学習 : Machine Learning 数学 : 數學 設計 : Design


書的LIST 請連到cookpad 的 blog 去看喔。 我就不複製貼上到我這裡來了,裡面的書都是日文的, 有一些是日文原著,有一些是從英文翻譯成日文的,所以有一些可以找英文版來看噢。 新卒ソフトウェアエンジニアのための技術書100冊 http://techlife.cookpad.com/entry/2015/03/31/093000

關於view 及helper內跨model 查詢及 ORM Object 轉換為array 的作法, View Partial 筆記

| Comments

最近在處理一個需求,如果我們有三個 model , 三個table 分別叫做 patents and splitwordAs and splitwordBs 第一個表是用來存所有的查詢資料,包括 id , apply_number, patent_content (文章的欄位) …etc .. 不過這個表不重要,不是這次的重點 第二、三個表是用來存切割文章用的關鍵詞,但是因為又有必須分階段切割段落的需求,還區分為兩大類, 所以就有splitwordAs & splitwordBs 這兩個表用來存放關鍵詞。
第二、三個表和第一個表是沒有任何的 association 關聯性的。 一開始為了到底要怎麼在 Controller 內去查詢不同的 model 資料感到痛苦萬分,一直覺得無法完成,原本的觀念以為 abc controller 只能存取 abc model,後來才發現觀念錯誤,根本就異常的簡單。這代表著觀念不清楚就會花很多冤枉路一直查詢。當然如果一個controller 內充滿了去查詢各式各樣的model好像也很不正確,應該可以用別種手法去更精簡的撰寫。 似乎是叫做 decorator or presenter 的 design pettern ? 不太確定是否有相關。

言歸正傳,其實只要在 patent controller 裡面下查詢的code,那麼,在 patent 的 view & helper 內就可以任意使用了

1
2
3
4
5
def show
(前略)
   @first_key = Keyworda.where.not('keyworda' => nil).order('priority asc').pluck(:keyworda)
 @second_key = Keywordb.where.not('keywordb' => nil).order('priority asc').pluck(:keywordb)
end

而且透過 pluck 的 method, 可以去透過 activerecord 去抓出我們要的欄位內容,比如說,我這邊只需要每個表裡面的每一個關鍵詞, pluck 會回傳為一個 array , 方便我們之後運用。 這邊還要注意, order 的 method 要放在 where之後,最後才能接 pluck 的method ,否則會出現錯誤。

另外,透過這次的實驗,我才知道 controller 裡面去指揮 activereocrd 取到資料之後, view 不用說,當然可以直接用這資料。這次發現 helper 也可以用,真是太棒了。我原本以為 controller 的資料一定必須得先傳去 view , 然後我們必須透過 call method 的方式把 值當作參數 傳入 helper 內的 method 。 如果真得必須這樣做,那實在是太麻煩了。 在查詢的過程當中,反而發現其實大多數人比較有問題的是,如何讓屬於 view 的 helper 可以讓 controller 或 model 用呢?

另外筆記一下 view 裡面去 render 兩個區塊,分別是放在不同目錄下的 partial 檔案 我的需求是, 我有一個主版面,要同時顯示兩個不同 model 的 table 資料, 或是 要同時顯示兩個 form 。

1
2
3
4
5
6
7
8
9
<h1> 增加斷詞組 </h1>
<div class="row">
  <div class="col-md-6">
    <%= render :partial => "keywordas/new", locals: {keyworda: @keyworda} %>
  </div>
   <div class="col-md-6">
    <%= render :partial => "keywordbs/new", locals: {keywordb: @keywordb} %>
  </div>
</div>

這段code表示在另外兩個目錄 keywordas , keywordbs 有 _new.html.erb 這樣的 partial file Partial file 要記得底線!!!

keywordas/html _new.html.erb

1
2
3
4
5
<%= form_for(keyworda, url: keywordas_path) do |f| %>
    <%= f.label :keyworda  %>
    <%= f.text_field :keyworda %>
    <%= f.submit " 增加第一步斷詞 ", class: "btn btn-large btn-primary" %>
<% end %>

from rails guide 的說明,放在這邊一起讀作為註解 ,否則每次貼的 code 變數名稱與值都是相同的命名,實在搞不清楚哪個是哪個。

3.4.6 區域變數

要在局部頁面裡使用區域變數,在呼叫局部頁面時使用 :as 選項:

1
<%= render partial: "product", collection: @products, as: :item %>

這樣修改以後,可以在局部頁面裡,用 item 來存取到 @products 集合裡的成員。也可以使用 locals: {} 選項,給任何的局部頁面,傳入隨意的區域變數。

1
2
<%= render partial: "product", collection: @products,
           as: :item, locals: {title: "Products Page"} %>

在這個情況裡,局部頁面裡可以存取到 title 區域變數,值是 “Products Page"。 Rails 也給傳入集合的局部頁面,提供了一個計數器變數。名稱是集合名加上 _counter。譬如在算繪 @products 時,可以在局部頁面裡使用 product_counter,來知道局部頁面被算繪了幾次。但不能和 as: :value 選項一起使用。

補充說明 collection: 的意思是

3.4.5 算繪集合 局部頁面在算繪集合時非常有用。當使用 :collection 選項,會把集合的每個元素插入的局部頁面裡:

html index.html.erb

1
2
<h1>Products</h1>
<%= render partial: "product", collection: @products %>

html _product.html.erb

1
<p>Product Name: <%= product.name %></p>

當局部頁面傳入複數形式的集合時,可以在局部頁面裡透過與局部頁面同名的變數來存取到集合的成員。上例裡,局部頁面是 _product,product 則是當下被算繪的實體。

算繪集合有簡寫形式。假設 @products 是 product 實體的集合,則在 index.html.erb 可以這麼寫:

1
2
<h1>Products</h1>
<%= render @products %>

發生在 View內的each 迴圈 - Rails N+1 Queries 問題解法

| Comments

前幾天在 Rails Tuesday 聚會的時候,與一個朋友在討論 code ,本來是在討論 many to many 的 association 時,要如何在中介的table 當中去查詢到另一個table的欄位資料, 例如,有一個table叫做,使用者,一個table叫做商品,我們有使用者可以有很多商品,商品也可以有很多使用者,是多對多的關係,因此我們可能會create 一個中介 table 叫做 user_product ,裡面就只有 user_id & product_id 兩個欄位, 但是如果我們希望可以拿到 product.name 時該怎麼做呢? 或者是如果我們在中介 table 內增加一欄了,那要怎麼拿出這一欄呢? 本篇文章沒有要討論這個問題。

因此拿出了 intermediate 課程當中的購物車的 code , 當打開放入購物車的 index 頁面 code 時,朋友問我說在view 裡面使用 .each 然後還去關聯另一個table 這樣會造成一直重複查詢,我才想到這可能有n+1 query (N+1查詢) 的問題。 經過百思不得其解的奮鬥之後,初步上得到在 view file 內可以使用 includes 的功能去關聯原先沒有拿到的欄位資料,而避免進入到 each 的 iterate 的時候,會重複request 。 以下就是簡單的筆記。

看了兩天的 ActiveRecord Association 發現真的好多東西需要去理解~ html 原本的 cart index view

1
2
3
4
5
6
7
8
9
 <tbody>
        <% current_cart.cart_items.each do |item| %>
        <tr>
            <td>
              <%= link_to(item.product.title, admin_product_path(item.product)) %>
            </td>
            <td> <%= item.product.price %> </td>
        </tr>
  </tbody>      

原本的 N + 1 queries

1
2
3
4
5
6
7
8
9
10
11
12
Started GET "/carts" for 175.184.245.33 at 2014-07-31 07:13:00 +0000
Processing by CartsController#index as HTML                                                                                                                                                       
  Cart Load (0.3ms)  SELECT  "carts".* FROM "carts"  WHERE "carts"."id" = 20 LIMIT 1
  CartItem Load (0.2ms)  SELECT "cart_items".* FROM "cart_items"  WHERE "cart_items"."cart_id" = ?  [["cart_id", 20]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 8]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 7]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 6]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 5]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 4]]
  Product Load (0.2ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 3]]
  Product Load (0.1ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 2]]
  Product Load (0.2ms)  SELECT  "products".* FROM "products"  WHERE "products"."id" = ? LIMIT 1  [["id", 1]]

在 .each 前面,在我們要做 iterate 的 object 後面增加一個 include 作法,讓 Rails 處理 Query 的時候,可以一併一次就先做聯集把 Product 的 detail 資料一起查出來,這樣後面就不用連續查 8 次了,

後來的 cart index view , 增加 : includes(:product)

1
2
3
4
5
6
7
8
9
 <tbody>
        <% current_cart.cart_items.includes(:product).each do |item| %>
        <tr>
            <td>
              <%= link_to(item.product.title, admin_product_path(item.product)) %>
            </td>
            <td> <%= item.product.price %> </td>
        </tr>
  </tbody>

我們可以看到 query 已經從前面的 8+1 變成 剩下 3 個 query , 這麼看可能感受還好,但是當這個畫面是類似查詢訂單那種一撈就是20筆, 40筆, 100筆等地記錄,確實就會大大的把query 從101個降到剩3個而已。

經過增加一個include 後的 query

1
2
3
  Cart Load (0.3ms)  SELECT  "carts".* FROM "carts"  WHERE "carts"."id" = 20 LIMIT 1
  CartItem Load (0.2ms)  SELECT "cart_items".* FROM "cart_items"  WHERE "cart_items"."cart_id" = ?  [["cart_id", 20]]
  Product Load (0.4ms)  SELECT "products".* FROM "products"  WHERE "products"."id" IN (8, 7, 6, 5, 4, 3, 2, 1)

其實我也想過 includes 應該是要放在 controller 或 model 裡面,因為畢竟是要處理 request for data 的動作,怎麼會放在view 裡面? 但目前因為這個購物車網站的 controller 內, current_cart 的 object 是放在 application_controller 裡, view 裡面的 @current_cart.cart_items 是靠著 model 的 association 成立的,所以一時之間就找不到地方去放這個 includes(:product)

includes 的用法還有很多,都是為了實現 eager loading ,避免 N+1 queries 的作法。 可以看官方文件 rails api ActiveRecord 裡面的 Eager loading of associations 章節 http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-belongs_to 或簡單一點的官方文件 rails guide ActiveRecord Query 裡面的 N + 1 查询的解决办法 http://guides.ruby-china.org/active_record_querying.html

這篇簡單的文章是講放在 controller 裡面的舉例作法 http://www.sitepoint.com/silver-bullet-n1-problem/

另外值得一提的是, eager loading 也不是聖杯,因為一次做額外的關聯table,會把一大堆東西一次查詢出來,放到 object內,因此memory會被占掉,要怎麼找到一個平衡,就是 performance tuning 的技巧了,待未來有學習到時,再另行寫一篇筆記吧。

補充說明:

另外還有一個用法是 joins . 那麼與 includes 的差別在哪呢? 從上述的說明可以看到使用 includes 的時機是,當我們希望先從 db query 兩個關聯的資料表時,在view 裡面 iterate 的時候不希望造成重複的 query . 那是因為我們需要讀取關聯後的另一個 table 的欄位資料,如上述的 product.title 欄位。

但是,如果我們僅僅需要關聯,而讀取的時候不需要讀取另一個table 的欄位資料時,我們則使用 joins 就好了。 可以看這篇的說明 includes vs joins differences in rails

明顯的兩段code 就可以感覺出來了

需要讀取 person table 的 name 的欄位, 所以要使用 includes
1
2
3
4
5
Company.includes(:persons).where(:persons => { active: true } ).all

@companies.each do |company|
     company.person.name
end

不需要 person table 裡面的欄位,所以使用 joins

1
2
3
4
5
Company.joins(:persons).where(:persons => { active: true } ).all

@companies.each do |company|
     company.name
end

Agile Web Development With Rails 4 - Error Handling 錯誤或例外處理

| Comments

書 p125頁,裡面提到,當處理 error handling / exception handling 的時候,有兩個動作可以做

  1. we’ll log the fact to an internal log file using Rails’logger facility.
  2. Second, we’ll redisplay the catalog page along with a short message to the user (something along the lines of “Invalid cart”) so they can continue to use our site.

先說明呼叫 invalid_cart() 這個 action 的作法

CartsController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class CartsController < ApplicationController
  before_action :set_cart, only: [:show, :edit, :update, :destroy]
  rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart

    private
  # ...
    def set_cart
      @cart = Cart.find(params[:id])
    end

    def invalid_cart
      logger.error "Attempt to access invalid cart #{params[:id]}"
      redirect_to store_url, notice: 'Invalid cart'
    end

end

當 set_cart 的method 被呼叫的時候,會去執行 Cart.find 的呼叫,當發生找不到的時候,就是 exception 發生了 因此隨即就會去呼叫 invalid_cart 的 method, (因為這個controller一開頭就已經宣告了 rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart 因此這個 method 執行的時候,第一步會先log起來,存在 development.log in the log directory 第二步就會發動把 user 的頁面導回去首頁,並且通過 notice: 'Invalid cart 的flash 方式,告訴使用者錯誤訊息是什麼。

延伸閱讀 http://guides.rubyonrails.org/debugging_rails_applications.html#the-logger

Agile Web Development With Rails 4 - Include CurrentCart (使用SessionID)

| Comments

在處理把商品放入購物車的時候,我們可以看到 Agile Web Development with Rails 4 裡面的範例是把創造一個購物車拉出去放在 moudle 裡面,然後當需要使用的 controller 要用的時候,會include 包進來,而不會寫死在controller裡面,我猜應該是為了保留彈性與延展性,希望書後面會再提到這個優點是什麼。 跟intermediate 課程的做法不太一樣

書裡面 module 的程式碼如下

current_cart.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#---
# Excerpted from "Agile Web Development with Rails",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material, 
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose. 
# Visit http://www.pragmaticprogrammer.com/titles/rails4 for more book information.
#---
module CurrentCart
  extend ActiveSupport::Concern

  private

    def set_cart
      @cart = Cart.find(session[:cart_id])
    rescue ActiveRecord::RecordNotFound
      @cart = Cart.create
      session[:cart_id] = @cart.id
    end
end

controller 程式碼如下, 所以當我們在把勾選的商品要放入到購物車的時候,會要 create 一個 LineItems 的物件,再把資料存到這個物件內 因此在這個動作前要先去驗證是否已經有購物車的物件的存在,沒有的話就創造一個出來。 我認為這邊的思維很重要,因為是很抽象,而新手很難聯想到的。

LineItemsController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class LineItemsController < ApplicationController
  include CurrentCart
  before_action :set_cart, only: [:create]

 def create
    product = Product.find(params[:product_id])
    @line_item = @cart.line_items.build(product: product)

    respond_to do |format|
      if @line_item.save
        format.html { redirect_to @line_item.cart,
          notice: 'Line item was successfully created.' }
        format.json { render action: 'show',
          status: :created, location: @line_item }
      else
        format.html { render action: 'new' }
        format.json { render json: @line_item.errors,
          status: :unprocessable_entity }
      end
    end
  end

Agile Web Development With Rails 4 - Before_destroy

| Comments

看到在處理刪除產品的時候,在model裡面要多一個 callback 去檢查 購物車裡面的資料 用來確定使用者現在的購物車內沒有這個商品,不然會很嚴重阿,後台把產品刪掉了 結果客戶走流程走去結帳了,那不就會噴ERROR出來?

這是在上 intermedia class 的時候沒被提到的,趕緊筆記下來,這也就是使用者故事(USER STORY)裡面應該會疏忽的;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
before_destroy :ensure_not_referenced_by_any_line_item

private
before_destroy :ensure_not_referenced_by_any_line_item
 private
    # ensure that there are no line items referencing this product
    def ensure_not_referenced_by_any_line_item
      if line_items.empty?
        return true
      else
        errors.add(:base, 'Line Items present')
        return false
      end
    end

Agile Web Development With Rails 4 - 快取緩存 Cache

| Comments

在讀 Dave Thomas 的這本 Agile Web Development with Rails 4 時,有看到以前不懂的觀念跟技巧,筆記下來。

因為做一個購物網站,當使用者很多的時候,會不斷的發出 http request 要求看產品頁面,所以導致我們的app server 就要不斷的去 request 資料庫。 但實際上,我們的產品資訊,在極短的時間內不會一直變動資料,比如說產品的名稱、說明、圖片價格等等,因此延伸出了上 cache 的功能。如果 rails server 去看一下產品資訊的更動時間沒動過,那就直接把cache的資料打回去給 web server 吐回去給使用者;如果有更新過,那就 request 資料庫,要求重新把新的資料傳上來,然後 cache 然後傳回去給 web server .

實際上怎麼做呢? 1 config/envrionments/development.rb 裡面有 config.action_controller.perfomr_caching 的選項,設為 true. 重開 server 2 在 model 的檔案內增加下列的一個 method . 這邊是以產品table 即 Product model 為範例

1
2
3
def self.latest
  Product.order(:updated_at).last
end

3 接著我們在我們的 view file 裡面,增加一段檢測更新狀態的 CODE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<h1>Your Pragmatic Catalog</h1>
<% cache ['store', Product.latest] do %>
  <% @products.each do |product| %>
    <% cache ['entry', product] do %>
      <div class="entry">
        <%= image_tag(product.image_url) %>
        <h3><%= product.title %></h3>
        <%= sanitize(product.description) %>
        <div class="price_line">
          <span class="price"><%= number_to_currency(product.price) %></span>
        </div>
      </div>
    <% end %>
  <% end %>
<% end %>

這邊用了一個 rails 所提供的一個技巧,叫做 [ Russian Doll Caching ] 俄羅斯娃娃快取(緩存) 因為我們做了兩層的 cache 設定,使用 [] 中括號 第一層命名為 store , 把整個 Product 的 array 依照更新時間排序後 cache 起來 第二層命名為 entry , cache 每一個 product 的元素 這麼做之後,rails 會打理一切剩下的工作,細節可以來看 rails guide 上的解說 及 DHH 的一篇文章 caching-with-rails @ rails guide how-key-based-cache-expiration-works

另外先大推一下 xdite 這篇文章,在講 DHH 如何設計了 cache-digest 的功能使用 MD5 HASH SOURCE CODE 來判斷到底要不要讓 cache expired . 非常厲害 cache-digest-new-strategy

本來想嘗試讀一下 rails guide 上的解說,發現有點太難懂了 先暫時不詳細解釋 [] 中括號的內容,以及如何清掉cache & update cache的原理。 讀懂後回來再補上…

最後,要注意的是,我們在開發階段的時候,其實還是要把前述在 config file裡面的值先改回 false, 否則,當我們在調整及修正 view 的template 或內容時,如調整位置、調整表格、CSS等等… 會發現怎麼按 refresh 都沒有用 那是因為 product 的資料沒有更新的時候, webserver 就直接吐快取回給 browser了,他就不去重新request rails server了,這樣反而會造成開發的時候的困擾。

Sublime Setting 設置文件中文解說

| Comments

本篇來自這裡 Sublime Text 2 設置文件詳解 ,作者是中國的 IT開發者: 金三胖

做個備份。 無侵權之用意。

看來想駕馭好這軟件不弄清楚配置文件是不行了,周末找了時間把配置文件的每條配置信息都加上了中文注釋,現在貼出來和大家共享,裏面有解釋不清楚的也歡迎大家夥來互相討論:)

另外,這也是我的第一篇博客,以後我會多寫些前端方面的文章和大家共享,歡迎一起討論:)

Preferences.sublime-settings文件:

// While you can edit this file, it’s best to put your changes in // “User/Preferences.sublime-settings”, which overrides the settings in here. // // Settings may also be placed in file type specific options files, for // example, in Packages/Python/Python.sublime-settings for python files. { // Sets the colors used within the text area // 主題文件的路徑 “color_scheme”: “Packages/Color Scheme – Default/Monokai.tmTheme”,

// Note that the font_face and font_size are overriden in the platform // specific settings file, for example, “Preferences (Linux).sublime-settings”. // Because of this, setting them here will have no effect: you must set them // in your User File Preferences. // 設置字體和大小,必須在Settings-User裏重寫,這裏設置沒有任何效果 “font_face”: “Consolas”, “font_size”: 12,

// Valid options are “no_bold”, “no_italic”, “no_antialias”, “gray_antialias”, // “subpixel_antialias” and “no_round” (OS X only) // 字體選項:no_bold不顯示粗體字,no_italic不顯示斜體字,no_antialias和no_antialias關閉反鋸齒 // subpixel_antialias和no_round是OS X系統獨有的 “font_options”: [],

// Characters that are considered to separate words // 在文字上雙擊會全選當前的內容,如果裏面出現以下字符,就會被截斷 “word_separators”: “./\()\”‘-:,.;<>~!@#$%^&*|+=[]{}`~?”,

// Set to false to prevent line numbers being drawn in the gutter // 是否顯示行號 “line_numbers”: true,

// Set to false to hide the gutter altogether // 是否顯示行號邊欄 “gutter”: true,

// Spacing between the gutter and the text // 行號邊欄和文字的間距 “margin”: 4,

// Fold buttons are the triangles shown in the gutter to fold regions of text // 是否顯示代碼折叠按鈕 “fold_buttons”: true,

// Hides the fold buttons unless the mouse is over the gutter // 不管鼠標在不在行號邊欄,代碼折叠按鈕一直顯示 “fade_fold_buttons”: true,

// Columns in which to display vertical rulers //列顯示垂直標尺,在中括號裏填入數字,寬度按字符計算 “rulers”: [],

// Set to true to turn spell checking on by default // 是否打開拼寫檢查 “spell_check”: false,

// The number of spaces a tab is considered equal to // Tab鍵制表符寬度 “tab_size”: 4,

// Set to true to insert spaces when tab is pressed // 設为true時,縮進和遇到Tab鍵時使用空格替代 “translate_tabs_to_spaces”: false,

// If translate_tabs_to_spaces is true, use_tab_stops will make tab and // backspace insert/delete up to the next tabstop // translate_tabs_to_spaces設置为true,Tab和Backspace的刪除/插入作用於制表符寬度 // 否則作用於單個空格 “use_tab_stops”: true,

// Set to false to disable detection of tabs vs. spaces on load // false時禁止在載入的時候檢測制表符和空格 “detect_indentation”: true,

// Calculates indentation automatically when pressing enter // 按回車時,自動與制表位對齊 “auto_indent”: true,

// Makes auto indent a little smarter, e.g., by indenting the next line // after an if statement in C. Requires auto_indent to be enabled. //針對C語言的 “smart_indent”: false,

// Adds whitespace up to the first open bracket when indenting. Requires // auto_indent to be enabled. // 需要启用auto_indent,第一次打開括號縮進時插入空格?(沒測試出來效果…) “indent_to_bracket”: true,

// Trims white space added by auto_indent when moving the caret off the // line. // 顯示對齊的白線是否根據回車、tab等操作自動填補 “trim_automatic_white_space”: true,

// Disables horizontal scrolling if enabled. // May be set to true, false, or “auto”, where it will be disabled for // source code, and otherwise enabled. // 是否自動換行,如果選auto,需要加雙引號 “word_wrap”: false,

// Set to a value other than 0 to force wrapping at that column rather than the // window width // 設置窗口內文字區域的寬度 “wrap_width”: 0,

// Set to false to prevent word wrapped lines from being indented to the same // level // 防止被縮進到同一級的字換行 “indent_subsequent_lines”: true,

// Draws text centered in the window rather than left aligned // 如果沒有定義過,則文件居中顯示(比如新建的文件) “draw_centered”: false,

// Controls auto pairing of quotes, brackets etc // 自動匹配引號,括號等 “auto_match_enabled”: true,

// Word list to use for spell checking // 拼寫檢查的單詞列表路徑 “dictionary”: “Packages/Language – English/en_US.dic”,

// Set to true to draw a border around the visible rectangle on the minimap. // The color of the border will be determined by the “minimapBorder” key in // the color scheme // 代碼地圖的可視區域部分是否加上邊框,邊框的顏色可在配色方案上加入minimapBorder鍵 “draw_minimap_border”: false,

// If enabled, will highlight any line with a caret // 突出顯示當前光標所在的行 “highlight_line”: false,

// Valid values are “smooth”, “phase”, “blink”, “wide” and “solid”. // 設置光標閃動方式 “caret_style”: “smooth”,

// Set to false to disable underlining the brackets surrounding the caret // 是否特殊顯示當前光標所在的括號、代碼頭尾閉合標記 “match_brackets”: true,

// Set to false if you’d rather only highlight the brackets when the caret is // next to one // 設为false時,只有光標在括號或頭尾閉合標記的兩端時,match_brackets才生效 “match_brackets_content”: true,

// Set to false to not highlight square brackets. This only takes effect if // match_brackets is true // 是否突出顯示圓括號,match_brackets为true生效 “match_brackets_square”: false,

// Set to false to not highlight curly brackets. This only takes effect if // match_brackets is true // 是否突出顯示大括號,match_brackets为true生效 “match_brackets_braces”: false,

// Set to false to not highlight angle brackets. This only takes effect if // match_brackets is true // 是否突出顯示尖括號,match_brackets为true生效 “match_brackets_angle”: false,

// Enable visualization of the matching tag in HTML and XML // html和xml下突出顯示光標所在標簽的兩端,影響HTML、XML、CSS等 “match_tags”: true,

// Highlights other occurrences of the currently selected text // 全文突出顯示和當前選中字符相同的字符 “match_selection”: true,

// Additional spacing at the top of each line, in pixels // 設置每一行到頂部,以像素为單位的間距,效果相當於行距 “line_padding_top”: 1,

// Additional spacing at the bottom of each line, in pixels // 設置每一行到底部,以像素为單位的間距,效果相當於行距 “line_padding_bottom”: 1,

// Set to false to disable scrolling past the end of the buffer. // On OS X, this value is overridden in the platform specific settings, so // you’ll need to place this line in your user settings to override it. // 設置为false時,滾動到文本的最下方時,沒有緩沖區 “scroll_past_end”: true,

// This controls what happens when pressing up or down when on the first // or last line. // On OS X, this value is overridden in the platform specific settings, so // you’ll need to place this line in your user settings to override it. // 控制向上或向下到第一行或最後一行時發生什麼(沒明白也沒試出來) “move_to_limit_on_up_down”: false,

// Set to “none” to turn off drawing white space, “selection” to draw only the // white space within the selection, and “all” to draw all white space // 按space或tab時,實際會產生白色的點(一個空格一個點)或白色的橫線(tab_size設置的制表符的寬度),選中狀態下才能看到 // 設置为none時,什麼情況下都不顯示這些點和線 // 設置为selection時,只顯示選中狀態下的點和線 // 設置为all時,則一直顯示 “draw_white_space”: “selection”,

// Set to false to turn off the indentation guides. // The color and width of the indent guides may be customized by editing // the corresponding .tmTheme file, and specifying the colors “guide”, // “activeGuide” and “stackGuide” // 制表位的對齊白線是否顯示,顏色可在主題文件裏設置(guide,activeGuide,stackGuide) “draw_indent_guides”: true,

// Controls how the indent guides are drawn, valid options are // “draw_normal” and “draw_active”. draw_active will draw the indent // guides containing the caret in a different color. // 制表位的對齊白線,draw_normal为一直顯示,draw_active为只顯示當前光標所在的代碼控制域 “indent_guide_options”: [“draw_normal”],

// Set to true to removing trailing white space on save // 为true時,保存文件時會刪除每行結束後多餘的空格 “trim_trailing_white_space_on_save”: false,

// Set to true to ensure the last line of the file ends in a newline // character when saving // 为true時,保存文件時光標會在文件的最後向下換一行 “ensure_newline_at_eof_on_save”: false,

// Set to true to automatically save files when switching to a different file // or application // 切換到其它文件標簽或點擊其它非本軟件區域,文件自動保存 “save_on_focus_lost”: false,

// The encoding to use when the encoding can’t be determined automatically. // ASCII, UTF-8 and UTF-16 encodings will be automatically detected. // 編碼時不能自動檢測編碼時,將自動檢測ASCII, UTF-8 和 UTF-16 “fallback_encoding”: “Western (Windows 1252)”,

// Encoding used when saving new files, and files opened with an undefined // encoding (e.g., plain ascii files). If a file is opened with a specific // encoding (either detected or given explicitly), this setting will be // ignored, and the file will be saved with the encoding it was opened // with. // 默認編碼格式 “default_encoding”: “UTF-8″,

// Files containing null bytes are opened as hexadecimal by default // 包含空字節的文件被打開默認为十六進制 “enable_hexadecimal_encoding”: true,

// Determines what character(s) are used to terminate each line in new files. // Valid values are ‘system’ (whatever the OS uses), ‘windows’ (CRLF) and // ‘unix’ (LF only). // 每一行結束的時候用什麼字符做終止符 “default_line_ending”: “system”,

// When enabled, pressing tab will insert the best matching completion. // When disabled, tab will only trigger snippets or insert a tab. // Shift+tab can be used to insert an explicit tab when tab_completion is // enabled. // 設置为enabled時,在一個字符串間按Tab將插入一個制表符 // 設置为true時,按Tab會根據前後環境進行代碼自動匹配填補 “tab_completion”: true,

// Enable auto complete to be triggered automatically when typing. // 代碼提示 “auto_complete”: true,

// The maximum file size where auto complete will be automatically triggered. // 代碼提示的大小限制 “auto_complete_size_limit”: 4194304,

// The delay, in ms, before the auto complete window is shown after typing // 代碼提示延遲顯示 “auto_complete_delay”: 50,

// Controls what scopes auto complete will be triggered in // 代碼提示的控制範圍 “auto_complete_selector”: “source – comment”,

// Additional situations to trigger auto complete // 觸發代碼提示的其他情況 “auto_complete_triggers”: [ {“selector”: “text.html”, “characters”: “<”} ],

// By default, auto complete will commit the current completion on enter. // This setting can be used to make it complete on tab instead. // Completing on tab is generally a superior option, as it removes // ambiguity between committing the completion and inserting a newline. // 設为false時,選擇提示的代碼按回車或點擊可以輸出出來,但選擇true時不會輸出而是直接換行 “auto_complete_commit_on_tab”: false,

// Controls if auto complete is shown when snippet fields are active. // Only relevant if auto_complete_commit_on_tab is true. // auto_complete_commit_on_tab必須为true,控制代碼提示的活躍度(沒明白…) “auto_complete_with_fields”: false,

// By default, shift+tab will only unindent if the selection spans // multiple lines. When pressing shift+tab at other times, it’ll insert a // tab character – this allows tabs to be inserted when tab_completion is // enabled. Set this to true to make shift+tab always unindent, instead of // inserting tabs. // 設置为false,使用Shift + tab總是插入制表符 “shift_tab_unindent”: true,

// If true, the selected text will be copied into the find panel when it’s // shown. // On OS X, this value is overridden in the platform specific settings, so // you’ll need to place this line in your user settings to override it. // 選中的文本按Ctrl + f時,自動复制到查找面板的文本框裏 “find_selected_text”: true,

// // User Interface Settings //

// The theme controls the look of Sublime Text’s UI (buttons, tabs, scroll bars, etc) // Data\Packages\Theme – Default\Default.sublime-theme控制軟件的主題 “theme”: “Default.sublime-theme”,

// Set to 0 to disable smooth scrolling. Set to a value between 0 and 1 to // scroll slower, or set to larger than 1 to scroll faster // 滾動的速度 “scroll_speed”: 1.0,

// Controls side bar animation when expanding or collapsing folders // 左邊邊欄文件夾動畫 “tree_animation_enabled”: true, // 標簽頁的關閉按鈕 “show_tab_close_buttons”: true,

// OS X 10.7 only: Set to true to disable Lion style full screen support. // Sublime Text must be restarted for this to take effect. // 針對OS X “use_simple_full_screen”: false,

// Valid values are “system”, “enabled” and “disabled” // 水平垂直滾動條:system和disabled为默認顯示方式,enabled为自動隱藏顯示 “overlay_scroll_bars”: “system”,

// // Application Behavior Settings //

// Exiting the application with hot_exit enabled will cause it to close // immediately without prompting. Unsaved modifications and open files will // be preserved and restored when next starting. // // Closing a window with an associated project will also close the window // without prompting, preserving unsaved changes in the workspace file // alongside the project. // 熱推出功能!退出時不會提示是否保存文件,而是直接退出 // 下次打開軟件時,文件保持退出前的狀態,沒來得及保存的內容都在,但並沒有真實的寫在原文件裏 “hot_exit”: true,

// remember_open_files makes the application start up with the last set of // open files. Changing this to false will have no effect if hot_exit is // true // 軟件使用最後的設定打開文件,hot_exit为true時沒有效果 “remember_open_files”: true,

// OS X only: When files are opened from finder, or by dragging onto the // dock icon, this controls if a new window is created or not. // 針對OS X “open_files_in_new_window”: true,

// Set to true to close windows as soon as the last file is closed, unless // there’s a folder open within the window. This is always enabled on OS X, // changing it here won’t modify the behavior. // 針對OS X “close_windows_when_empty”: true, // 哪些文件會被顯示到邊欄上 // folder_exclude_patterns and file_exclude_patterns control which files // are listed in folders on the side bar. These can also be set on a per- // project basis. “folder_exclude_patterns”: [“.svn”, “.git”, “.hg”, “CVS”], “file_exclude_patterns”: [“.pyc”, “.pyo”, “.exe”, “.dll”, “.obj”,“.o”, “.a”, “.lib”, “.so”, “.dylib”, “.ncb”, “.sdf”, “.suo”, “.pdb”, “.idb”, “.DS_Store”, “.class”, “.psd”, “.db”], // These files will still show up in the side bar, but won’t be included in // Goto Anything or Find in Files “binary_file_patterns”: [“.jpg”, “.jpeg”, “.png”, “.gif”, “.ttf”, “.tga”, “.dds”, “.ico”, “.eot”, “.pdf”, “.swf”, “.jar”, “*.zip”],

// List any packages to ignore here. When removing entries from this list, // a restart may be required if the package contains plugins. // 刪除你想要忽略的插件,需要重启 “ignored_packages”: [“Vintage”] }