丹哥的技術培養皿

A blogging framework for hackers.

關於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”] }

Ruby (Rails) 內運算式 (Operator)的優先順序 " && " , " == " , " || " 符號

| Comments

本篇的概念來自於這篇stackover flow 的文章,筆記一下,否則每次看code都會卡住看不懂,不習慣背後的道理就都會很不直覺阿。 When do we use the “||=” operator in Rails ? What is its significance?

例如: 我們可能會看到這種程式碼,一開始的時候都是無法理解這代表什麼含意,是做了什麼判斷? 傳回什麼值? 誰跟誰比對? 優先順序又是什麼?

1
2
@_current_user ||= session[:current_user_id] &&
      User.find(session[:current_user_id])

我們要先講一個觀念是當看到 a ||= b 這種code的時候,要知道它其實是 a || a = b , 而他所代表的含意是如果 a 本身自己是 nil or false 的時候,則他就會等於 b 的值。 反之,如果 a 不是 nil , 不是 false的時候,那就保持原狀。 詳細的解說可以看這一篇文章 What Ruby’s ||= (Double Pipe / Or Equals) Really Does

讓我們來拆解這段程式碼,一步一步的解讀,

第一步

1
@_current_user ||= ( session[:current_user_id] && User.find(session[:current_user_id]) )

等同於

1
@_current_user || @_current_user = ( session[:current_user_id] && User.find(session[:current_user_id]) )

代表的是如果 @_current_user 是 nil or false 的時候, 就指定為||= 符號右邊的數值,但是右邊又是另一個判斷式,我們進一步解讀。

第二步

第二個觀念: 這一串 ( session[:current_user_id] && User.find(session[:current_user_id]) ) 是什麼意思?
這邊有一個小技巧,如果 session[:current_user_id] 不是 nil or false 那麼 ruby 就會去判斷 && 符號的右邊的內容 : 如果 User.find(session[:current_user_id]) 也不是 nil ,這樣的話就會傳回 User.find(session[:current_user_id]) 的值。

可以參考這一篇 : Ruby’s && operator for params 術語上似乎是叫做 short circuit evaluation.

第三個觀念: 優先順序的問題 我們可以從 ruby document 看到運算子的優先順序,我將這個表複製如下,規則是 越上面的越先運算、執行、判斷 ( PS: ruby version 2.1.2 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
!, ~, unary +
**
unary -
*, /, %
+, -
<<, >>
&
|, ^
>, >=, <, <=
<=>, ==, ===, !=, =~, !~
&&
||
.., ...
?, :
modifier-rescue
=, +=, -=, etc.
defined?
not
or, and
modifier-if, modifier-unless, modifier-while, modifier-until
{ } blocks

第三步

所以啦,回到原本的程式碼 當這一串 ( session[:current_user_id] && User.find(session[:current_user_id]) ) 先判斷完之後,就知道是 nil 或者是 && 右邊的值,接著再看 || 的左邊 @_current_user 如果不是 nil or false 就決定是保持原狀,否則就把 User.find(session[:current_user_id]) 指定給 @_current_user 。

持續更新 Sync (Follow) Fork下來的repo

| Comments

以下是更新的指令,前提是你的版本中沒有尚未commit的東西,不然會更複雜一些。 git remote add upstream “來源的git位置” git fetch upstream git checkout master git merge upstream/master 或 git rebase upstream/master git push origin (這一步一般是用在更新 github,當然如果有另外的 git server 也有用)

建議還可以參考鴉七寫得很詳細的 git rebase 解說 Git-rebase 小筆記