Rails學習筆記


本文是一個Rails新手的學習筆記,主要是對過去一個月中學習內容的總結,包括:css

  • Agile Web Development with Rails 4html

  • Rails 101web

  • Rails for Zombies算法

水平有限, 錯誤再所不免(這也是我寫出來的緣由啦 :D), 還請諸位多多指教.數據庫

學習體驗

Rails 的開發速度很是快,但學習速度是很慢的。DHH曾經提到過:相比learnability,他更看重usability。DHH的15分鐘開發博客程序確實激動人心,可是不通過全面的學習,想要作出合格可用的產品是不現實的。bootstrap

概述

學習Rails究竟是在學習什麼? 在我看來,主要是如下幾個:數組

瞭解處理用戶請求的完整路徑

這是學習任何一個web框架,都要完成的任務。Rails相比其餘框架,須要學習的點就是MVC 和 routes 文件的寫法。瀏覽器

MVC

Rails框架強制你使用MVC模式進行開發,那什麼是真正的MVC呢?那就是:ruby

用戶請求的永遠是controller,服務器返回給瀏覽器的永遠是view.服務器

以用戶註冊爲例,直覺的作法是用戶點擊首頁的註冊連接,轉到有表格的view,用戶填寫信息以後submit,請求轉到controller中的特定action,處理請求以後根據結果轉回註冊頁面或者自動登陸到首頁。

這種作法並無徹底符合MVC,由於有經過硬編碼的連接實現的view到view的跳轉,Rails的作法是這樣的:

點擊首頁的註冊連接,跳轉到users_controller的 new,這個action計算用戶須要填寫的信息,初始化一個空的對象,轉到new_html這個view,用戶填寫表格以後submit,請求轉給create這個action,處理請求以後根據結果 redirect_to 到 new (重定向會讓瀏覽器再次向服務器發起對controller下 new 的請求)或者自動登陸到首頁。

永遠是 view -> controller -> view 的處理流程。

這並非一個好例子,或許我該舉一些「不在view中寫業務邏輯、保持controller精簡」之類的最佳實踐,可是它們都被舉爛了,我也就再也不多說,我舉得這個例子或許能讓你對MVC有多一些的思考。

routes

routes.rb這個文件無疑是任何 Rails 項目的靈魂, 我獲得的建議是: 若是你打算研究一個 Rails 開源項目, 要打開的第二個文件永遠是routes.rb, 第一個固然是Gemfile啦, :D.

這個文件的兩個基本做用:

  • 提供了new_user_path這樣的寫法,避免出現硬編碼的連接
  • 把用戶地址欄的連接與controller中的action相對應.

這就牽扯到了Rrails的一個重要特性: restful. 之前寫Servlet, 常常會有/getUser?id=3這樣的連接, restful的想法是, 既然自己已是GET請求了, 連接何須再有get呢. Rails就按照restful的思路, 把連接變成了: GET users/:id. 在使用該連接的時候, 用users_path(@user)這樣的寫法來傳遞id這個參數.

restful固然還有更"高大上"的做用, 例如分佈式系統下的容錯性之類的, 可是對於新手, 這樣理解設計者的初衷就行了: 就是將updateUserInfo?id=3 deleteUser?id=4這樣的連接, 轉成PUT users/3 DELETE users/3.

新手常常搞不清楚path的寫法, 尤爲是出現各類nested resources的時候, 在終端輸入rake routes, 就能夠看到了正確寫法了.

ORM

主要學習的點包括migration, seeds.rb, 表關係及查詢語句的編寫.

migration

SQL無疑是很是好用的語言, 但有個致命的肯定: 三五人的小團隊, 沒有專門的DBA, 如何保證全部開發者的數據庫設計是一致的? 固然, 有許多的作法能夠解決這個問題, 這些作法中migration是最好的之一.

它的基本思路, 就是用ruby代碼來作數據庫設計(增長新的表, 增長索引, 改變某列的數據類型), 每次修改都放在單獨的良好命名的文件之中, 只要將其歸入版本管理, 經過rake db:migrate命令, 就能夠保證全部開發者的數據庫設計師一致的.

同時藉助seeds.rb文件, 保證全部開發者的測試數據是一致的.

表關係

也就是表之間的主外鍵關係以及三範式的知足. 在我短暫的Rails開發旅程中, 大多數時間都花費在數據庫設計上, 也使我對

程序 = 算法 + 數據結構

這一論斷愈來愈信服.

Rails 提供了諸多功能方便開發者申明表之間的主外鍵關係, 包括has_many belongs_to has_many_through等, 合理地利用該特性能夠幫助你寫出可讀性很是好的代碼, 例如在這樣的主外鍵設計之下:

class Group < ActiveRecord::Base
    has_many :group_users
    has_many :members, :through => :group_users, :source => :user 
end

class GroupUser < ActiveRecord::Base
    belongs_to :user
    belongs_to :group
end

class User < ActiveRecord::Base
    has_many :group_users
    has_many :participated_groups, :through => :group_users, :source => :group
end

能夠用current_user.participated_groups這樣十分天然的語句來獲取當前登陸用戶參加的社團.

查詢語句

這一點沒什麼好說的, 就兩點:

  • 注意避免一些明顯的錯誤, 例如N+1
  • where返回的是數組,find_by返回的是找到的第一個值

經常使用gem的使用

這也是學習Rails中的很重要的一個點, 選擇Rrails是爲了快速開發, 那爲何不更快一點呢. devise讓你一份實現登錄與註冊, 郵箱驗證, 密碼找回; bootstrap-rails讓你兩分鐘提高網站的設計感; will_paginate讓你再也不操心分頁的實現....

其餘

固然還有許多重要的點要學習, 可是都沒有上面幾個那樣有提綱挈領的做用. 都放到下一節當細節說吧.

tips

工具

主要就是Sublime Text了, 有幾個插件很是好用, 明顯提升效率. 我裝了Emmet, ERB SnippetsRuby on Rails snippets三個插件, 主要是實現

  • PE而後tab 能夠自動生成<%= -%>
  • ER而後tab 能夠自動生成<% %>
  • ul>li*2>a而後tab能夠打出:
<li><a href=""></a></li>
<li><a href=""></a></li>
  • 還有許多神奇的功能, has_many之類的, 你們自行查看其官網的cheat_sheet

ORM

scope的使用

scope :graveyard, where(:show_location => true , :location => 'graveyard')

add_index

給列添加索引, 以提高性能

主外鍵關係的一些額外屬性

  • 能夠顯示地設置外鍵
class Tweet < ActiveRecord::Base
  has_one :location, :dependent => :destroy, :foreign_key => :tweeter_id
end
class Location < ActiveRecord::Base
  belongs_to :tweet, :foreign_key => :tweeter_id
end
  • dependent: :destroy來設置外鍵依賴
  • 避免 N+1 issue
    使用 Zombie.includes(:brain).all ,來代替直接Zombie.all, 而後view中each打印zombie.brain, 以提高性能

  • update_attributes和update是同樣的

rake的使用

參考這裏 rake, 也可使用rake -T查看

  • 使用rake db:schema:dump, 能夠從如今的數據庫建立schema文件
  • 學習開源項目時, 使用rake db:setup. 三件事: 建立數據庫, 讀取schema文件, 從'seeds.rb'導入種子數據

controller

統一處理各action中的異常

  • module定義在concerns/current_cart_rb中, 一方面是其中的方法在各個controller中共享, 其次防止其中的方法被當作action來調用. 注意該文件夾下的文件都是autoload的, 不須要顯式地寫include.
  • 注意是如何使用callback使set_cart執行的.
class CartsController < ApplicationController
  before_action :set_cart, only: [:show, :edit, :update, :destroy]
  rescue_from ActiveRecord::RecordNotFound, with: :invalid_cart 

  def index
    @carts = Cart.all
  end

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

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

防止doble render

def new
    if @cart.line_items.empty?
      redirect_to store_url, notice: 'Your cart is empty.'
      return
      #redirect_to不會自動return
    end

    @order = Order.new
  end

一些澄清

-before_filter 就是舊版本中的 before_action
- build 就是new的別名
- save!會引起RecordInvalid異常, 而save會返回boolean
- nil?判斷對象是否存在, 不存在的對象都是nil的; empty?判斷是否爲空字段,好比一個字符串是否爲空串,或者一個數組中是否有值;object.blank? 至關於o
bject.nil?||object.empty?`
- 在befor類的鉤子函數中,返回FALSE則不執行before後跟的操做, 此處容易出bug

model

錯誤信息

能夠用errors.add(:base, 'Line Items present')添加錯誤信息, 在controller中能夠經過@line_item.errors來獲取.

view

body的class

能夠經過<body class='<%= controller.controller_name %>'>來命名body的class, 方便scss的書寫.

select的實現

#view中
<%= f.select :pay_type, Order::PAYMENT_TYPES, prompt: 'Select a payment method' %>
# model中
PAYMENT_TYPES = ['Check', 'Credit Card', 'Purchase Order']
validates :pay_type, inclusion: PAYMENT_TYPES

一些澄清

  • button_to默認是POST請求, link_to默認是GET請求.
相關文章
相關標籤/搜索