[Rails] 小技巧 Rails Tips
keywords: helper
[TOC]
常用
- [判斷內容是空或有]([Rails] 判斷內容是空或有.md)
- [判斷物件有哪些方法?上一層是誰?屬於哪個型態]([Rails] 判斷物件有哪些方法?上一層是誰?屬於哪個型態.md)
其他
清除 Rails 內的快取(Cache)
# 必要時需要手動 rm -rf 刪除
$ rails log:clear
$ rake tmp:clear
$ rm -rf tmp/cache tmp/data tmp/miniprofiler
檢視 secret / credentials 檔案
$ EDITOR="sublime -w" rails credentials:edit
$ EDITOR=vim rails credentials:edit
SCSS 載入圖片
在 SCSS 中使用圖片 url 時,需使用 image-url
這個 helper,否則開發環境看得到,但到 production mode 就會炸掉:
.banner {
background: image-url('banner.png') center center no-repeat;
}
How to reference images in CSS within Rails 4 @ StackOverflow
如果是在 Webpacker 中要用圖片,則是要使用 asset-image()
這個 helper:
// 圖片路徑 app/assets/images/event-header-bg.png
background-image: asset-image('event-header-bg');
自訂錯誤訊息樣式(Customize Field Error)
# ./config/environment.rb
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
html_tag.html_safe
end
Customize Field Error in Rails 5 @ Ruby Plus
Methods
陣列(Array)
元素分群
# 把 @products 這個大陣列以 3 個為一組拆成小陣列,不足的用 false 填補
Array.in_groups_of(3, false){|group|}
取得陣列元素數目
# 取得陣列元素數目建議使用 size 效能 較好(替代掉 count 或 length)
Order.all.size
產生 Secure Token
在建立某欄位的時候自動產生 Token:
# ./app/models/event.rb
class Event < ApplicationRecord
validates_uniqueness_of :authentication_token
before_create :generate_unique_secure_token
private
def generate_unique_secure_token
# return if self.authentication_token.present?
loop do
token = SecureRandom.base58(24)
self.authentication_token = token
break token unless Event.find_by(authentication_token: token)
end
end
end
驗證該 token 是否符合格式:
# ./app/controllers/api_controller.rb
def token_format_valid?(authentication_token)
base58_alphabet = ("0".."9").to_a + ("A".."Z").to_a + ("a".."z").to_a - ["0", "O", "I", "l"]
(authentication_token.split('') - base58_alphabet).blank? && authentication_token.size == 24
end
View Helper
<!-- 刪減多於字數 -->
<%= truncate("Once upon a time in a world far far away", length: 17) %>
<!-- 檢驗路由是否為 -->
<% current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2') %>
<% current_page?('/shop/checkout') %>
- ActionView URL Helpers @ RailsAPI
常用變數
<!-- in erb file -->
<%= root_url %> <!-- 取得根網址 -->
<%= controller_name %> <!-- 取得 controller 名稱 -->
<%= action_name %> <!-- 取得 action 的名稱 -->
動態 i18n
# zh-TW.yaml
form:
en: 英文
zh-TW: 中文
<!-- 直接使用 t("form.#{...}") 即可動態使用 i18n
<% I18n.available_locales.each do |locale| %>
<%= t("form.#{locale}") %>
<% end %>
避免 HTML Encode
<%= raw(data) %>
<%= string.html_safe %>
如果有傳入 local_variable 才使用該變數
keywords: local_assigns
<!-- Without default value -->
<% if local_assigns.has_key? :headline %>
Headline: <%= headline %>
<% end %>
<!-- With default value -->
<% some_local = default_value if local_assigns[:some_local].nil? %>
Optional local variables in rails partial templates @ StackOverflow
使用 content_tag 自定義元素
在 do...end
block 中如果希望有 if
判斷,可以在 end
後面使用 if
<%= content_tag :p, 'Hello', class: 'mb-1' %>
<!-- 使用 do end block -->
<%= content_tag :p, class: 'mb-1' do %>
<i class="fas fa-fw fa-globe"></i>
<%= link_to 'Website', @event.website, class: 'pl-2', target: :_blank %>
<% end if @event.website.present? %>
使用 radio 和 label
keywords: f.radio_button
, label_tag
<div class="form-group">
<label>Gender</label>
<div class="w-100"></div>
<div class="custom-control custom-radio custom-control-inline">
<%= f.radio_button :gender, 'male', checked: @personal_info.gender == 'male', class: 'custom-control-input' %>
<%= label_tag 'personal_info_gender_male', 'Male', class: 'custom-control-label' %> </div>
<div class="custom-control custom-radio custom-control-inline">
<%= f.radio_button :gender, 'female', checked: @personal_info.gender == 'female', class: 'custom-control-input' %>
<%= label_tag 'personal_info_gender_female', 'Female', class: 'custom-control-label' %> </div>
</div>
選擇國家的下拉選單(select)
# app/helpers/common_helper.rb
def country_options(selected = nil, options = nil, locale = I18n.locale)
options =
if options.blank?
ISO3166::Country.translations(locale).sort.to_h.invert.entries
else
ISO3166::Country.translations(locale).select{ |k, v| options.include?(k) }.sort.to_h.invert.entries
end
options_for_select(options, selected)
end
<!-- _form.html.erb -->
<div class="form-group">
<%= f.label :country, class: 'col-form-label fz-13 text-muted' %>
<%= f.select(:country, country_options(event.country), { prompt: '- 請選擇 -' }, { class: 'custom-select' }) %>
<div class="invalid-feedback feedback-icon">
<i class="fas fa-exclamation-triangle"></i>
</div>
</div>
在 partial / template 中使用 yield 和 content_for
這是 partial / template,在這裡面的 <%= yield %>
一定要寫在其他有名稱的 <%= yield :foo %>
之前,才能把要 render 它的樣板的內容帶進來:
<%# ./app/views/shared/_navbar.html.erb %>
<div>
<!-- 沒有名稱的 yield 一定要寫在最前面 -->
<%= yield %>
<ul class="left">
<%= yield :before_left_nav %>
<li>Some items ...</li>
<%= yield :after_left_nav %>
</ul>
<ul class="right">
<%= yield :before_right_nav %>
<li>Some items ...</li>
<%= yield :after_right_nav %>
</ul>
</div>
這是要 render 上面這個 partial 的樣板,在 render
後 do ... end
中的所有內容都會帶入到 _navbar.html.erb
裡面的 <%= yield %>
裡面,在從中看要對應到哪個有名稱的 yield
:
<%# ./app/views/pages/tester.html.erb %>
<%= render 'shared/navbar', navbar_class: 'navbar-darker-background' do %>
<% content_for :before_left_nav do %>
<li class="nav-item">
<%= link_to 'Before', products_path, class: "nav-link #{'active' if action_name == 'products'}" %>
</li>
<% end %>
<% content_for :after_left_nav do %>
<%# ... %>
<% end %>
<% content_for :before_right_nav do %>
<%# ... %>
<% end %>
<% content_for :after_right_nav do %>
<%# ... %>
<% end %>
<% end %>
Controller
在 controller 轉譯某個 partial view
# app/controllers/*_controller.rb
# 把轉譯出來的 html 塞到 innerHTML 這個變數
innerHTML = ApplicationController.renderer.render(partial: 'projects/collects/issue_next', locals: { app: @app, collect: @collect, issue: @issue, editable: true })
建立一個 controller helper 讓所有 controller 都能用
將要共用的 helper 寫在 application_controller.rb
中:
# ./app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# STEP 2:補上 helper_method
helper_method :current_cart
protected
# STEP 1: 將要共用的 method 寫在這
def current_cart
@current_cart ||= Cart.find_by!(user: current_user)
rescue ActiveRecord::RecordNotFound
Cart.create(user: current_user)
end
end
使用 transaction
透過 Model.transaction
或 @instance.transaction
當 model 變更的過程中如果有任何錯誤發生,則這個變更會整個 rol back 回去。
# ShoppingMall/app/controllers/admin/orders_controller.rb
def shipped
if @order.may_mark_as_shipped?
begin
Order.transaction do
@order.mark_as_shipped!
redirect_to admin_orders_url, notice: '訂單狀態變更成功'
end
rescue
redirect_to admin_orders_url, alert: '訂單狀態變更失敗'
end
else
redirect_to admin_orders_url, alert: '訂單狀態錯誤'
end
end
動態 Hash key 、動態呼叫 methods、移除空值
keywords: dynamic hash key,
dynamic invoke methods
, reject empty item
- 使用
Hash[<key>, <value>]
,可以動態建立 Hash 的 Key - 使用
project.send(<method>)
可以動態的呼叫 project 的方法
available_medias = ["website", "facebook", "twitter", "instagram"]
project_links = available_medias.map do |media|
Hash[media, project[:"#{media}"]] if project[:"#{media}"]
end.reject(&:blank?)
一次取出所有 id 的值(ids)
User.all.ids # 回傳陣列,可以取得所有 User 的 id
Models
使用 params
keywords: fetch
, dig
# 一般取得 params 的方式
params[:product][:remove_photos_index]
# 如果有定義 params 的話
product_params
def product_params
params.fetch(:product, {}).permit(:name, :brief, :description)
# params.require(:product).permit(:name, :brief, :description)
end
# 使用 dig 取得 order 裡面的 promo_code 時,如果 promo_code 不存在時程市不會炸掉
unless params.dig(:order, :promo_code).blank?
promo_code = PromoCode.where(code: params.dig(:order, :promo_code)).first
@order.promo_code = promo_code
end
可參考 Action Controller Overview
操作 params
keywords: update model
, params
, delete
刪除某個 params
# 刪除 product params 中的 photos
params[:product].delete(:photos)
把 params 存起來
# 使用 update
@product.update(product_params)
# 使用 assign_attribute
@product.assign_attributes(product_params)
使用 Safe Navigation Operator (&.
)
keywords: &.
不用擔心前面的值是 nil
時會拋錯誤(undefined method for nil:NilClass
),類似於前面存在時才會跑後面:
@person&.spouse&.name
@person.spouse.name if @person && @person.spouse # 等同於上面那句
但是不可作為判斷:
nil.nil? # true
nil&.nil? # nil
nil&.foobar # nil
What does ampers and dot mean in ruby @ Stack Overflow
Turbolinks
在特定頁面關掉 turbolinks
<meta name="turbolinks-visit-control" content="reload" />