目錄部分
The Rails Command Line
model
Active Record Basics
Active Record Migrations
Active Record Validations
Active Record Callbacks
Active Record Associations
Active Record Query Interface
Active Model Basics
controllers
Action Controller Overview
Rails Routing from the Outside In
digging deeper
Active Job Basics
The Asset Pipeline
正文部分
The Rails Command Line
rails server -e production -p 4000 #指定環(huán)境和端口
rails new demo --database=mysql #指定數據庫
rails g scaffold Article name:string #使用scaffold,資源為單數形式
rails g controller Articles index #使用controller為復數形式
rails g model Article name #使用model為單數形式
#和其他表格進行關聯,tag has_many artilces
rails g modle Artilce name:string tag:references
#升級到rails5.0之后,之前的rake命令變?yōu)閞ails命令(成為默認的執(zhí)行命令)
rake routes => rails routes
rake demo.rake =>rails demo.rake #執(zhí)行rake任務,在lib/tasks中
Active Record Basics
model和數據庫名稱要相互對應
#普通單數形式
model:Article #首字母大寫
database:articles
#非普通單數形式
model:Person
database:people
#多字段形式
model:LineItem #camelcase形式
database:line_items
主鍵和外鍵
主鍵:id,自動生成
外鍵:singularized_table_name_id,比如article_id
#在命名終端生成主鍵和外鍵
rails g model Article name:string #自動生成主鍵id
rails g model Comment name:string article:references #生成外鍵article_id(注意格式)
Active Record Migrations
創(chuàng)建表單和字段,刪除表單
#rails g model Product name:string
#生成表單和字段
class CreateProducts < ActiveRecord::Migration[5.0]
def change
create_table :products do |t|
t.string :name
t.timestamps
end
end
end
#rails g migration remote_product
class RemoteProcut < ActiveRecord::Migration[5.0]
def change
drop_table :prefaces
end
end
生成字段、刪除字段
#默認已經有articles這個表單,生成name字段
#rails g migration addnametoarticle
class Addnametoarticle < ActiveRecord::Migration[5.0]
def change
add_column :articles, :name, :string
end
end
#刪除字段
#rails g migration removenameformarticle
class Addnametoarticle < ActiveRecord::Migration[5.0]
def change
remove_column :articles, :name, :string
end
end
增加索引,刪除索引
#默認已經有articles的name字段
#rails g migration addindextoname
class Addindextoname < ActiveRecord::Migration[5.0]
def change
add_index :articles, :name
end
end
#查看數據中那些字段存在索引
show index from articles
#查看指定字段是否存在索引
show index from articles where column_name like 'name'
#刪除索引
class Removeindex < ActiveRecord::Migration[5.0]
def change
remove_index :articles, :name
end
end
建立關聯和刪除關聯
#比如已經有artilces,需要實現article has many tags,因此建立tag model 最簡單的關聯:
rails g model Tag name:string article:references
#上面在tags表中會出現article_id這個字段
#使用migration進行references
rails generate migration AddUserRefToProducts user:references
class AddUserRefToProducts < ActiveRecord::Migration[5.0]
def change
add_reference :products, :user, index: true, foreign_key: true
end
end
#刪除關聯
自己沒有實現
Active Record Callbacks
callback是指回調的意思,當對象狀態(tài)改變的時候,這些回調方法會被觸發(fā),對象的狀態(tài)有三種,create,update,destroy,回調的方法調用形式如下:
#第一種,使用預定義的回調函數,里面定義塊的內容
class User < ApplicationRecord
before_validation do |user|
puts "this is the test"
end
end
#第二種,使用預定義的回調函數注冊回調方法
class User < ApplicationRecord
before_validation :test
#可以指定某個action上使用驗證
before_validation :test, on: :create
#可以指定多個action上使用驗證
before_validation: test, on: [:create, :update]
private
def test
puts "this is the test"
end
end
對象改變觸發(fā)的回調函數如下:
#當create時
before_validation
after_validation
before_save
around_save
after_save
before_create
around_create
after_create
after_commit/after_rollback
#當update時
before_validation
after_validation
before_save
around_save
after_save
before_update
around_update
after_update
after_commit/after_rollback
#當destroy時
before_destroy
around_destroy
after_destroy
after_commit/after_rollback
#initialize
after_initialize #對象初始化和從數據庫中l(wèi)oad對象時觸發(fā)
#find
after_find #從數據庫中l(wèi)oad對象時觸發(fā)
#touch
after_touch #對象進行touch時觸發(fā)
具體的觸發(fā)和不觸發(fā)方法的動作
#create, update, destroy
create
create!
decrement!
destroy
destroy!
destroy_all
increment!
save
save!
save(validate: false)
toggle!
update_attribute
update
update!
valid?
#find
all
first
find
find_by
find_by_*
find_by_*!
find_by_sql
last
#跳過觸發(fā)動作
decrement
decrement_counter
delete
delete_all
increment
increment_counter
toggle
touch
update_column
update_columns
update_all
update_counters
Active Record Query Interface
獲得單個值
find #通過id獲得值,也通過通過id數組獲得值
Article.find(1)
Article.find([1,2]) #Article.find(1,2)也是可以的
take #沒有知名排序取值
Article.take #默認取1個值
Article.take(2) #取兩個值
first #默認按照id的升序取第一個值
Article.first #如上
Article.first(3) #取前三個值
Article.order(:name).first #改變默認排序,取第一個值
last #默認按照id的降序取第一個值
Artilce.last
Artilce.last(3)
Article.order(:name).last
find_by #通過相應的字段屬性取值
Article.find_by(name: "jayzen")
處理大數據量的情況,目的是避免超出內存容量
#方法依次是
find_each
find_in_batch
#可選的參數包括
batch_size
start
finish
#兩者的區(qū)別
find_each #每次數據傳遞一部分
find_in_batch #數據一次性傳遞,但是按照數組塊的形式
使用where進行條件查詢
#純字符查詢會出現SQL注入問題,不采用
Client.where("orders_count = '2'")
#數組查詢
#單個數組查詢
Client.where("orders_count = ?", params[:orders])
#多個數組查詢
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
#使用占位符進行數組查詢
Client.where("created_at >= :start_date AND created_at <= :end_date",
{start_date: params[:start_date], end_date: params[:end_date]})
#使用hash條件查詢
#單個hash值
Client.where(locked: true)
#范圍hash值
Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
#數組hash值
Client.where(orders_count: [1,3,5])
#使用not條件查詢
Client.where.not(locked: true)
使用order進行排序
#單個字段使用order進行排序,并且使用desc或者asc指定排序方式
Article.order(name: :desc)
#多個字段使用order進行排序
Article.order(name: :desc, content: :asc)
#使用鏈式排序
Article.order(name: :desc).order(content: :asc)
使用select選擇特定的字段
#只選擇兩個字段內容
Article.select("name, content")
#選擇字段的屬性內容,不重復出現
Article.select("name").distinct
使用limit和offset指定返回的個數和開始的位置
Article.limit(5) #最多返回5條記錄,默認返回最初的5條
Article.limit(5).offset(30) #從第31條開始返回記錄,最多返回5條記錄
使用group進行分組
#獲得某一個時間點的訂單數量
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
#統計不同狀態(tài)值的累計量
Order.group(:status).count
使用having進行條件判斷
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)").having("sum(price) > ?", 100)
對條件進行改變
#unscope取消條件
Article.where('id > 10').limit(20).order('id
asc').unscope(:order) #取消order條件
#使用only重新制定選擇條件
Article.where('id > 10').limit(20).order('id desc').only(:order, :where)
#使用reorder進行條件重新制定
class Article < ApplicationRecord
has_many :comments, -> { order('posted_at DESC') }
end
Article.find(10).comments.reorder('name')
#使用reverse_order改變排列的方式
Client.where("orders_count > 10").order(:name).reverse_order
#使用rewhere改變where的排列方式
Article.where(trashed: true).rewhere(trashed: false)
返回空值
Article.none #返回空值
設置對象可讀,在寫入的時候會報錯
client = Client.readonly.first
client.visits += 1
client.save #會報錯ActiveRecord::ReadOnlyRecord exception
數據庫中實現join(inner join)和left join的區(qū)別
方式和rails中的joins和left_outer_joins一致
#join(inner join)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
INNER JOIN Orders
ON Persons.Id_P = Orders.Id_P
ORDER BY Persons.LastName
#結果:返回的完全匹配的值,即是必須滿足Persons.Id_P = Orders.Id_P
#left join(left outer join)
SELECT Persons.LastName, Persons.FirstName, Orders.OrderNo
FROM Persons
LEFT JOIN Orders
ON Persons.Id_P=Orders.Id_P
ORDER BY Persons.LastName
#首先返回Persons.Id_P = Orders.Id_P的值,如果Orders.Id_P不存在,則只返回左表中的值即可。
悲觀鎖(pessimistic lock)和樂觀鎖(optimistic lock)
optimistic lock:認為沖突比較少,用戶在檢查的更新的檢查lock_version的值時候變動,變動即返回錯誤。應用在實際沖突比較少的場景。舉例:rails中model表單存在可選的lock_version字段。
pessimistic lock:認為每一次更新都存在沖突,都有一個鎖定和開鎖的過程,這樣子程序開銷比較大,引用在沖突比較多的場景。
Rails Routing from the Outside In
作用:鏈接url到code;生成url
1、resource routing
resources :photos(復數資源)
GET /photos photos#index photos_path
GET /photos/new photos#new new_photo_path
POST /photos photos#create
GET /photos/:id photos#show
GET /photos/:id/edit photos#edit edit_photo_path(:id)
PATCH/PUT /photos/:id photos#update
DELETE /photos/:id photos#destroy photo_path(:id)
上面提到的都是復述資源,下面講述單數資源
resource :geocoder
GET /geocoder/new geocoders#new new_geocoder_path
POST /geocoder geocoders#create
GET /geocoder geocoders#show geocoder_path
GET /geocoder/edit geocoders#edit edit_geocoder_path
PATCH/PUT /geocoder geocoders#update
DELETE /geocoder geocoders#destroy
對于單例資源生成表格,需要制定url
form_for @geocoder, url: geocoder_path do |f|
...
對于namespace路由、嵌套路由、淺嵌套(shallow nesting)在文檔中有敘述,但是自己用到的不多,所以遇到的時候在參考guides即可。Routing concerns是將常用到的路由打包,以備后用。
增加的三個restful形式的路由
#member
resources :photos do
member do
get 'preview'
end
end
=>/photos/1/preview
preview_photo_url
params[:id]
#collection
resources :photos do
collection do
get 'search'
end
end
=>/photos/search
search_photos_url
#new
resources :comments do
get 'preview', on: :new
end
#/comments/new/preview
preview_new_comment_path
2、non resourceful routes
Active Job Basics
應用場景:在用郵箱注冊的過程中,給郵箱發(fā)送郵件這個動作應該放在后臺進行。
active job可以使用的adapter有很多,比如delayed job,resque和sidekiq,后兩者需要安裝redis,其中sidekiq是在resque基礎上的改進,但是性能方面要優(yōu)于resque,但是sidekiq的過程中要注意線程安全問題。
active job的其中一個adapter是sidekiq,而這個軟件需要安裝redis。
其中redis的安裝和啟動過程如下:
#目前redis的版本是3.2
brew install redis
#啟動redis
redis-server /usr/local/etc/redis.conf
其中sidekiq的安裝和啟動的過程如下:
#安裝就是添加gem
gem 'sidekiq'
#啟動就是終端輸入如下代碼
sidekiq
項目啟動之前要同時啟動redis和sidekiq,下面執(zhí)行代碼中內容:
#生成scaffold
rails g scaffold Article name:string
#生成job文件
rails g job add_article
#修改jobs/add_artilce_job.rb文件
class AddArticleJob < ApplicationJob
queue_as :default
def perform(*args)
sleep 10
10.times do |index|
article = Article.new
artilce.name = "name#{index}"
article.save
end
end
#修改app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def index
AddArticleJob.perform_later
@articles = Article.all
end
end
在沒有使用sidekiq作為adapter的情況下,會使用Active Job Inline作為默認的adapter,但是這adapter不是異步的,按照代碼執(zhí)行順序進行延后,但是如果采用sidekiq作為adapter的情況,可以異步,只需要進行如下的配置:
module JobDemo
class Application < Rails::Application
config.active_job.queue_adapter = :sidekiq
end
end
可以通過log的代碼關注執(zhí)行情況,
The Asset Pipeline
首先需要關注的是css文件和javascript文件如何進行組織的問題。
assets/stylesheets/application.css文件內容
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
assets/stylesheets/application.js文件
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
上文分別給出了application.js和application.css的說明,其中
//= require jquery
//= require jquery_ujs
//= require turbolinks
會從三個地方尋找對應的文件,分別是app/assets,lib/assets和vender/assets三個文件夾中,require_tree .。表示在app/assets文件中js和css文件會被自動合并到application.css或者application.js文件中,在lib/assets和vender/assets中的文件除非在application.js或者application.css文件中按照如上的方式被引入,不然無效。