Kurt Hsu's blog

The Rails developer in taiwan.


  • 首頁

  • 標籤

  • 分類

  • 歸檔

[Rails]用includes解決N+1 query

發表於 2018-09-10 更新於 2019-08-27 分類於 Rails

N+1 query幾乎是Rails一定會碰到的問題了XD
剛好買的書有提到就來玩一下, 其實先以server log到底什麼時候hit sql, 以及hit了幾次就能解決

問題發生

先假設User有3個Post, 我們很常在controller裡面寫

1
2
3
def index
@posts = Post.all
end

然後顯示這篇Post的User name會在view裡寫

1
2
3
<% @post.each do |post| %>
<%= post.user.name %>
<% end %>

這時候查看server log會發現hit了sql 4次, 這個就是N+1 query, 如果今天User有1000筆post就會hit 1001次query, 就會影響效能

1
2
3
4
Post Load (0.2ms)  SELECT "posts".* FROM "posts"
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]

解決方案by includes

簡單的解決方法先把User includes近來給Post即可

1
2
3
def index
@posts = Post.includes(:user).all
end

再來查看server log只hit 2次

1
2
Post Load (0.1ms)  SELECT "posts".* FROM "posts"
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? [["id", 1]]

觀察hit時間

其實觀察hit的時間也蠻重要的, 如果寫下列程式碼

1
2
3
4
5
6
7
def index
@posts = Post.all
binding.pry
Post.first
binding.pry
puts 'asd'
end

將會發現第一次binding.pry直接撈Post.all並不會hit sql, 而第二次binding.pry的Post.first有hit sql, 這似乎是rails lazy load做的事情, 如果撈資料還有可能串上更多orm撈資料的時候他並不會真的先衝進資料庫撈資料, 一直要到確定這筆資料要被使用了或者後面不能再串orm了就會hit sql, 所以也可以發現第二次binding.pry的Post.all還是沒有hit sql, 要一直到view開始each render這些post的時候才會去hit sql, 有這個資訊的話會更好去調sql上的效能

參考書籍
為你自己學Ruby on Rails2017.09初版

[Rails]利用config檔設定path

發表於 2018-08-22 更新於 2019-08-22 分類於 Rails

最近專案在實施前後端分離,所以把有關於前台的一切刪除,但會造成前端路徑會報錯,例如posts_path就爆炸了,所以我們要有替代方案,但這個替代方案前提是local和production的資料同步,至少local端的資料production上都有。

能用的狀況:

  1. 有production機台
  2. local的資料庫資料production必須都有

原理:

創造的路徑是直接連到production作為前後端分離實施中的替代方案

設定config

我在config資料夾下建立yml檔,檔名任意

asswt_host.yml
1
2
development: &DEV
host_domain: "http://domian.com"

設定了在開發環境下的變數
再來給環境config檔吃

development.rb
1
2
3
…
config.x.asset_hosts = config_for('asset_host').symbolize_keys
end

第一種方法利用routes.rb

routes.rb
1
resources :posts, constraints: { host: Rails.application.config.x.asset_hosts[:host_domain], port: "" }

此時view即可正常使用post_url
記得port改為空字串不然domain會被加上port

第二種方法利用helper

posts_helper.rb
1
2
3
4
5
def post_preview_url(post)
host = Rails.application.config.x.asset_hosts[:host_domain]
path = Rails.application.routes.url_helpers.post_path(Post.last)
URI.join(host, path).to_s
end

此時view即可正常使用post_preview_url(Post)

小結

看到這裡可能會想說嗯?那我設完routes就好何必多此一舉設定helper?

我這裡的應用情況是應急前後端分時關於前端路徑全爆的問題,所以routes裡的許多路徑是已存在的狀況要去修改

方法一是利用constraints直接去改舊的routes路徑
方法二則是利用舊的路徑生成helper使用
當然還是會有其他的做法就看怎麼規劃比較適合自己的專案

[Rails]利用 yield 指定javascript和css

發表於 2018-08-09 更新於 2019-08-22 分類於 Rails

最基本的使用就是在controller指定layout之後載入Template樣板:

application.html.erb
1
2
3
4
5
…
<body>
<%= yield %>
</body>
…

controller
1
2
3
4
5
6
7
class EventsController < ApplicationController
layout "special"

def index
…
end
end
event/index.html.erb
1
<p> Hello, world <p>

我們可以利用content_for自訂上述東西

application.html.erb
1
2
3
4
5
6
7
8
…
<body>
<%= yield :event %>
<%= content_for :sidebar do %>
<p> Hello, world <p>
<% end %>
</body>
…

會有一樣效果,當然通常content_for會是放在別的檔案這只是個概念,他也可以拿來做tag,許多人拿他來做一隻檔案專門加強SEO

1
2
3
4
5
6
7
<%= content_for :head do %>
<%= tag(:meta, :content => @event.name, :property => "og:title") %>
<%= tag(:meta, :content => @event.description, :property => "og:description") %>
<%= tag(:meta, :content => "article", :property => "og:type") %>
<%= tag(:meta, :content => @event.logo.url, :property => "og:image") %>
<%= tag(:meta, :content => event_url(@event), :property => "og:url") %>
<% end %>

怎麼指定載入Javascript和CSS呢?

application.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="zh-tw">
<head>
…
<%= yield :page_specific_css %>
…
</head>
<body>
…
<%= yield %>
…
<%= yield :page_specific_javascript %>
</body>

然後在該view例如剛剛的index

event/index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<p> Hello, world <p>
<% content_for :page_specific_css do %>
<script type="text/stylesheet”>
p {
color: red;
}
<%= stylesheet_link_tag "cssfile" %>
</script>
<% end %>
<% content_for :page_specific_javascript do %>
<script type="text/javascript">
console.log(“run script”)
<%= javascript_include_tag "jsfile" %>
</script>
<% end %>

這樣當進入該頁面可以直接寫入或載入外部檔案,如果該頁面的Template沒有這些也不會噴錯,就只是沒有page_specific_css和page_specific_javascript而已,如果重複宣告content_for相同的名稱他會把它合併不會覆蓋,十分方便。

值得小注意一下的地方是如果直接在application.html宣告content_for的話會先執行template裡的東西,然後一樣由上到下執行,可以多使用console.log測試一下就知道了。

參考文章
Rails實戰聖經

[Rails]cancan的load_and_authorize_resource

發表於 2018-08-08 更新於 2019-08-22 分類於 Rails , Gem

這邊簡單記錄一下第一次碰到load_and_authorize_resource的歷程,打這篇文章之前發現已經有大神把cancan講解得非常完美了,參考Cancan 實作角色權限設計的最佳實踐

load_and_authorize_resource在官方有提到的是等於

1
2
3
4
def load_and_authorize_resource
load_resource
authorize_resource
end

要不要拆開來用都是可以的,拆開不拆開他們都有很多不一樣的用法

load_resource

load_resource會幫你把所有的methods載入instance value,而載入哪一個則預設看controller的naming,例如PostsController則會載入@post

很智能的是它會判斷controller是什麼
例如是new則@post = Post.new
例如是show則@post = Post.find(params[:id])

authorize_resource

authorize_resource則是對resource的權限判斷
例如在Ability檔案裡有

1
2
3
can :read, Post
can :create, Post
can :update, Post

則在剛剛PostController使用這三個methods是沒問題的

這時候可能遇到需要在一個controller去判斷不同的model,那要怎麼判斷權限呢?

1
2
3
4
5
6
7
8
9
10
11
lass PostController < ApplicationController
authorize_resource :comment

def new
@comment = Comment.new
end

def show
@comment = Comment.find(params[:id])
end
end

authorize因為找不到instance value會自動產生Comment的instance value,雖然就可以在PostController判斷comment的權限,但還是寫清楚一點好

你可以把load_resource看成先製作好所有instance value
而authorize_resource則是去檢查current_user對於此instance value的權限
最直覺的當然就是

1
2
load_resource
authorize_resource

[Rails]update_attribute vs update_column

發表於 2018-08-08 更新於 2019-08-22 分類於 Rails

目前比較常看到更新資料用的有save、update、update_attribute和update_attributes方法

save預設會跑驗證,想跳過可以下

1
2
3
person.save(validates: false)
#或者直接
person.save(false)

update跟update_attributes其實做的事情一樣因為大部分的人會這樣寫

1
2
3
4
5
6
7
8
9
10
11
12
13
@person = Person.last
@person.update(person_pareams)
#使用update

@person.update_attributes(person_pareams)
#或使用update_attributes

@user = User.update(@user.id, user_avatar_params)
#update另一個update_attributes沒有但不建議的用法

#只單純User.update(@user.id, user_avatar_params)似乎不會寫進資料庫
#可以參考這篇stack overflow
#https://stackoverflow.com/questions/27684038/rails-paperclip-update-vs-update-attributes

比較特別的是update_attribute,他是直接跳過驗證強制更新單筆資料,跟update_attributes雖然很像但用途蠻不一樣的

1
2
person.update_attributes(age: 20)
#只能給一個參數

在維護上我會建議不使用update_attribute和update_attributes

第一:要更語意話不如單純使用save或update

第二:update_attribute跟update_attributes命名真的太像做的事情卻不一樣(update_attribute是直接跳過驗證更新資料庫的)

第三:update_attribute很有可能之後被淘汰了因為Rails團隊新增了update_colum去做更棒的命名區別

update_colum跟update_attribute做的事情一模一樣不須通過驗證,也只能傳單筆資料,更棒的是update_colums是可以傳多筆資料不需要驗證,至少他們做的事情是一樣了吧!

故真的要都不驗證的話不如寫

1
2
3
4
5
@person.save(validates: false)
#至少大家都看得懂

@person.update_colums(person_attributes)
#都加上s的話直傳單筆多比都ok

參考文章
iT邦技術文章(直接看update那邊)
關於save的api
關於update的api
關於update_columns的api

[Rails]routes scope 和 concern 運用

發表於 2018-08-02 更新於 2019-09-07 分類於 Rails , Routes

scope

最基本產生在某個資料夾的的routes

1
2
3
namespace :admin do
resources :articles
end

以index為範例$ rake routes可以看出

method verb path controller#method
admin_articles_path GET /admin/articles admin/articles#index

如果想把 /articles 路徑(path)不帶 /admin 前缀但依然要映射到 Admin::Articles 控制器上,可以用scope:

1
2
3
scope module: 'admin' do
resources :articles
end

得到的index:

method verb path controller#method
articles_path GET /articles admin/articles#index

還有下面幾種寫法可以得到上面相同的index結果:

1
resources :articles, module: 'admin'

1
2
3
scope '/admin' do
resources :articles
end

也有一種只改路徑(path)的方法

1
resources :articles, path: '/admin/articles'

method verb path controller#method
articles_path GET /admin/articles /articles#index

concern

concern其實可以把它當作routes用的變數,宣告後可以給各個地方重複使用,簡單例子:

1
2
3
4
5
6
concern :concerntest do
resources :articles
end
scope module: "admin" do
concerns :concerntest
end

得到的index:

method verb path controller#method
articles_path GET /articles admin/articles#index

小總結:
如果真的看不懂或不夠資深還是善用$ rake routes去把它印出來看,再來我覺得還是不要用太多方法去寫,並不會好維護到哪裡去,以中國有嘻哈的角度看寫起來也不會特別的skr,還是第一眼讓大家看懂最重要。

[Rails] routes 高級約束 (Advanced Constraints)

發表於 2018-08-01 更新於 2019-09-07 分類於 Rails , Routes

constraints是routes的一個約束方法,官方中文翻譯文件是說約束,但我覺得有點像是專屬於routes的if,如果成立才可以執行,例如最簡單的兩個例子:

1
match 'photos', to: 'photos#show', via: [:get, :post]

只要http verb不是指定的get或post就不行了

1
get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ }

一個可以利用正則去驗證id的概念

再來高級約束有什麼厲害的地方?
官方提供了一個很酷的例子,擋ip

1
2
3
4
5
6
7
8
9
10
11
12
13
class BlacklistConstraint
def initialize
@ips = Blacklist.retrieve_ips
end

def matches?(request)
@ips.include?(request.remote_ip)
end
end
 
Rails.application.routes.draw do
get '*path', to: 'blacklist#index', constraints: BlacklistConstraint.new
end

他的遊戲規則是constraints一定會去跑class的matches?得到一個bool來決定可不可以通過該routes。

而這個範例利用了class.new的特性先去自動執行initialize得到一個instance value(@ips)然後再自動去執行matches?

request則是內建的物件有很多方法可以用,remote_ip則是其中一個。

而routes設定get ‘*path’(所有路徑)的時候都會觸發,所以remote_ip有包含在Blacklist.retrieve_ips裡面則routes會把你導去啟動blacklist#index黑名單的controller,十分的酷炫。

為什麼研究到這個是因為公司的例子,一個rails專案裡面放兩個產品專案,然後利用routes偵測subdomain去決定要顯示什麼頁面:

1
2
3
4
5
…(略)
constraints(Subdomain::B) do
get “/welcome_b” => “welcome_b#index”
end
…(略)
1
2
3
4
5
6
7
8
9
10
11
12
13
module Subdomain
class A
def self.matches?(request)
request.subdomain.in? ["www", "staging", ""]
end
end

class B
def self.matches?(request)
request.subdomain.in? [“B”, “B-staging"]
end
end
end

所以直接偵測網址的subdomain裡有出現陣列字串就可以成立,那現在出現了一個問題,我要怎麼開發B網站呢?

localhost預設是一個沒有subdomain的domain,而沒有subdomain所以subdomain等於空字串是成立的,所以把A網站陣列裡的空字串改到B的陣列就可以嘍!

參考文章:
Rails Guides英文
Rails Guides中文
兩天同一篇

[Devops]iTerm + oh my zsh 美化終端機

發表於 2018-08-01 更新於 2019-08-22 分類於 Devops

iTerm是比內建終端機還好用的終端機工具,因為可以客製化很多東西,搭配oh-my-zsh的套件能讓美觀更進一步,且可以直接套用主題~

假設已經安裝好iTerm了,我們參考oh-my-zsh的官方文件開始執行以下步驟:

首先要先看有沒有安裝好Zsh($zsh —version可以檢查)

如果沒有安裝的話就用curl安裝

$ sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)”

安裝完之後重新打開iTerm如果視窗是顯示bash環境請轉換成zsh
$chsh -s /bin/zsh
重開後視窗應該要顯示zsh

安裝oh-my-zsh的東西
首先下載
$ git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh

我們要自己生成zsh的設定檔,位置是~/.zshrc,我們直接用官方的template比較省事
$ cp ~/.oh-my-zsh/templates/zshrc.zsh-template ~/.zshrc

我們希望用的是agnoster主題,官方有說明文件
發現要用這個主題要用Solarized顏色和Powerline-patched Meslo字型(https://github.com/powerline/fonts)

然後可以在剛剛的.zshrc檔案裡頂端加入一行
DEFAULT_USER=“xxxx”,xxxx可以輸入自己的英文名字
再來找到一行ZSH_THEME這個是設定主題的,如果被註解掉就把它解開設定主題如下
ZSH_THEME="agnoster”,agnoster就是主題名稱

每次修改完.zshrc可以輸入source ~/.zshrc刷新一下這個檔案再重開iTerm

再來可以去設定字形和顏色,打開iTerm後左上角
iTerm > Preference > Profiles
Colors > 可以設定顏色
Text > Change Font > 可以設定字型,建議設定Menlo for Powerline和14pt

應該就有漂亮的iTerm可以使用了!

[生活]從前端轉戰Rails後成為T客邦的一份子

發表於 2018-08-01 更新於 2020-03-07 分類於 生活 , 找工作

先不討論找到T客邦工作之前的多多少少甘苦談了,這大概是我人生做過最大筆的自我投資,目前也是最棒的投資之一。

其實一開始我的環境建立是蠻不順的XD,因為電腦是沒重灌過,造成很多離職同事裝的套件一直因為權限不讓我用,例如Homebrew之類的,這些還真的是我沒想過會撞到的牆QQ。

沒想到搞了將近兩天(有半天想說讓系統更新一下結果就是一下午…)之後才真的開始玩公司裡的Rails101,熟悉的Xdite教材但其實很老舊,碰了許多小地雷。

但真正比較悲劇的是這裡的新手訓練很有趣是給你一張票,票底下有很多子票就跟著教材的每一個章節跑,但我一開始忘了了就在一個分支一直做下去囧,還好發現的時候重做的速度很快。

那時候我覺得讓我最痛苦的地方大概是很不習慣deploy方面的debug,很多error都要去特定的資料夾看,還常常有看沒有懂,比起來直接開發網站真的是蠻幸福的事情。

101終於都弄完成了之後就是無限的codereview,老實說真的蠻酷的!!我也在兩天之內瘋狂的使用rebase,感覺現在很不害怕git版本控制了呀!

前前後後搞了快半個月才走出新手村,結果我第一張票是一位前輩做完然後叫我重做當練習,雖然不難但其實不是一張算小的票!底下的子票大概有十來個,而且還牽扯到角色權限問題。

所幸的是有很多其他的專案可以參考,所以也不然是要開發什麼全新功能的感覺,但就是要看的懂code,熟悉檔案架構還有認識一堆Gem,這真的蠻瘋狂的,一瞬間太多東西了真的很容易亂掉記後忘前的,開發的思維真的跟我之前前端差蠻多的(不過專案大小也有根本的差距,前公司的專案真的很小XD),還好同事很細心的教導我。

還有不習慣的一點是後端真的要蠻細心,有時候前端硬try自然慢慢的debug就可以拿到要的效果雖然到最後真的不好維護,但在後端的世界有時候想要硬try一個東西真的不是這麼容易,還要想做假資料,先想好各種情況,在檔案架構很大的情況下真的是很怕牽一髮動全身改了A檔案到底會不會影響到BCD檔案,但漸漸的習慣了專案架構之後其實這不算太大的問題了。

再來最可怕的大概就是要了解各種gem了,T客邦很厲害的是自己包了很多gem,那對於新人來說常常一個很特別的methods我要先猜是不是從對的hleper或者model定義而來,再來猜是不是外面的gem,最後再去找是不是T客邦自己有包的gem,如果不是多解一點票可能光要找功能就會花上許久時間。

整個一個月初下來的心得,T客邦真的是一個可以學習碰到很多東西的地方,專案架構整體至少比起我碰到的東西來說是相對很大的,但老舊的東西或多或少還是有自己覺得美中不足的地方想寫得更好,但我想目前更熟悉Rails,gem和deploy上的問題才是首要,一個月下來迅速的累積了不少技術債呀!

[Rails]基本Routes設計

發表於 2018-07-20 更新於 2019-09-07 分類於 Rails , Routes

在之前文章[Rails]簡單的掌握Routes, Rails在基本的resources使用上已經非常方便, 但在工作上第一次遇到shallow等用法的時候即使rake routes出來仍然一頭霧水為什麼會出現這樣的結果, 所以記錄一下自己研究後的觀念

此篇目的

  1. 理解Routes邏輯
  2. 嵌套資源(Nested resources)
  3. 認識shallow
  4. 認識namespace
  5. 漂亮的客製化例如 admin/posts/generator 這種route

理解Routes邏輯

先以最基本的resources :posts當範例

1
2
3
Rails.application.routes.draw do
resources :posts
end

$ rake routes得到的結果

Prefix Verb URI Pattern Controller#Action
posts GET /posts(.:format) posts#index
POST /posts(.:format) posts#create
new_post GET /posts/new(.:format) posts#new
edit_post GET /posts/:id/edit(.:format) posts#edit
post GET /posts/:id(.:format) posts#show
PATCH /posts/:id(.:format) posts#update
PUT /posts/:id(.:format) posts#update
DELETE /posts/:id(.:format) posts#destroy

Prefix

Prefix加上_path就是Rails的Route helper, 例如想要連去Post index就在view寫上
<%= link_to posts_path %>

再來如果看到Prefix為空, 例如如上表create動作Prefix為就空, 就代表Prefix與上一個相同, 但要注意Verb是什麼, link_to預設都是GET, 所以要連去create post view則寫
<%= link_to posts_path, method: :post %>

URI Pattern

URI Pattern就是網址, 前面就是自己的網域, (.:format)就是要render的格式, 可以是html, JSON等等在controller設定即可就不探討, 預設是html, 之後的表單就省略不寫(.:format)了

再來可以注意到像show的網址有:id這個參數, 就得帶想要看的id進去, 所以要連去第1篇post view則寫
<%= link_to post_path(1) %>

帶一個物件也可以, 我們很常在controller看到

1
2
post = Post.find(1)
redirect_to post_path(post)

Controller#Action

顧名思義就是找哪個controller的哪個action, 如果有namespace則要注意資料夾結構, 檔案要放對位置

嵌套資源(Nested resources)

在post下嵌套comment當範例

1
2
3
4
5
Rails.application.routes.draw do
resources :posts do
resources :comments
end
end

$ rake routes | grep comments我們先只看comment的結果

Prefix Verb URI Pattern Controller#Action
post_comments_index GET /posts/:post_id/comments comments#index
POST /posts/:post_id/comments comments#create
new_post_comments GET /posts/:post_id/comments/new comments#new
edit_post_comments GET /posts/:post_id/comments/:id/edit comments#edit
post_comments GET /posts/:post_id/comments/:id comments#show
PATCH /posts/:post_id/comments/:id comments#update
PUT /posts/:post_id/comments/:id comments#update
DELETE /posts/:post_id/comments/:id comments#destroy

照著上一個例子的邏輯沒什麼太大的問題, 唯一要注意的是較好的設計規範
嵌套資源不要超過一層
例如這個例子就是有一層了, 盡量不要再往下有resources, 難維護也比較沒意義

認識shallow

先理解shallow的設計邏輯:

  1. 只有該文章可以列出自己所有的comment, 並且new和create
  2. 在任何地方只要能找到該comment就可以show, edit, update, destroy他

只要簡單的寫一行即可做到這個邏輯

1
2
3
resources :posts do
resources :comments, shallow: true
end

其效果等同於

1
2
3
4
resources :posts do
resources :comments, only: [:index, :new, :create]
end
resources :comments, only: [:show, :edit, :update, :destroy]

或者這樣比較看得懂

1
2
3
4
resources :posts do
resources :comments, except: [:show, :edit, :update, :destroy]
end
resources :comments, only: [:show, :edit, :update, :destroy]

再來根據前面的例子試著$ rake routes結果吧!

認識namespace

最常使用的地方是後台的建構, 例如:

1
2
3
namespace :admin do
resources :posts
end

$ rake routes會發現所有東西都加上了admin

Prefix Verb URI Pattern Controller#Action
admin_posts GET /admin/posts admin/posts#index
POST /admin/posts admin/posts#create
new_admin_post GET /admin/posts/new admin/posts#new
edit_admin_post GET /admin/posts/:id/edit admin/posts#edit
admin_post GET /admin/posts/:id admin/posts#show
PATCH /admin/posts/:id admin/posts#update
PUT /admin/posts/:id admin/posts#update
DELETE /admin/posts/:id admin/posts#destroy

比較要注意的是Controller#Action的部分, 此時的controller要放在
app/controllers/admin/posts_controller.rb
而檔案裡面class name要改為

1
2
class Admin::PostsController
end

漂亮的客製化例如 admin/posts/generator 這種route

利用collection可以蠻方便客製化出想要的route, 但post複數以及單數兩種情況, 如果是複數的話利用即可

1
2
3
4
5
6
7
namespace :admin do
resource :posts do
collection do
get 'generator'
end
end
end

$ rake routes | grep generator結果如下:

Prefix Verb URI Pattern Controller#Action
generator_admin_posts GET /admin/posts/generator admin/posts#generator

即可快速的創造好複數的route, 但對於覺得generator應該是單數的post的人認為Prefix就會不ok, 比較簡單的單數解法就如下:

1
2
3
4
5
6
7
8
namespace :admin do
resource :posts
resource :post do
collection do
get 'generator'
end
end
end

簡化一點點:

1
2
3
4
5
6
namespace :admin do
resource :posts
resource :post do
get 'generator', on: :collection
end
end

$ rake routes | grep generator結果如下:

Prefix Verb URI Pattern Controller#Action
generator_admin_post GET /admin/post/generator admin/post#generator

比較要注意的是Controller#Action要的controller是單數的
app/controllers/admin/post_controller.rb

小結

當然routes還有很多寫法, 也很容易hack, 利如又想單數又想直接共用複數的controller也可以這樣寫:

1
2
3
4
5
6
namespace :admin do
resource :posts
resource :post do
get 'generator', on: :collection, to: 'admin/posts#generator'
end
end

甚至直接一行幹到底:

1
get 'admin/post/generator', to: 'admin/posts#generator', as: 'generator_admin_post'

不管想怎麼寫, 保持不斷的rake route查明結果吧!!

參考文章:
書籍 - 為你自己學Ruby on Rails2017.09初版
Rails 路由:深入淺出

1…456…18

Kurt Hsu

Progress One Percent Every Day
171 文章
55 分類
163 標籤
RSS
© 2020 Kurt Hsu
由 Hexo 強力驅動 v3.8.0
|
主題 – NexT.Muse v7.3.0