# Rails 經驗複習與提醒 (書上不會教的東西)
###### tags: `rails 經驗複習`
## Configs 設定
若是有設定區分不同環境, 請一定要使用 config 來設定, 否則你會寫一堆邏輯在程式內.
覆寫邏輯 config/initializers/add_files_to_configs.rb

## Circle Ci 隨機測試.
目前fulong 已經進了.
CircleCI 有針對較慢的 rspec 在後端加上
if run_rspec?
預設是 1/8 的機會才會跑到.
若是比較重要的 rspec 我會設成 if run_rspec?(4) 就是 1/4 的機率.
1. 目前只會在 master 跑全部的 test .
2. 這部分會節省 CI 可能會在 15m 內跑完.
3. 會省下不少 CircleCi 的費用, 節省 1/3 ~ 1/4
4. 需要觀察後續在加以調整, 後續可以按照重要程度與否調整比例.
https://github.com/SplashtopInc/be-app/commit/550e4de29f456b7ab04989be7d71499c51e46ec6
## Rspec CircleCi
Circle CI 有個避掉不跑的 tag
**special: true**

使用情境:
若是該測試在本機跑都是正常的, 但是上Ci後會開始有問題. 而且透過 SSH 連上 CI 去 debug.
若是透過 SSH 到CI 還是找不出問題的話, 可勉強使用該方法去 pass.
## change(usec: 0)
Time.now.change(usec: 0) 可以讓 milliseconds 變成 0
```ruby
pry(main)> Time.now
2023-03-15 15:24:09.49589 +0800
pry(main)> Time.now.change(usec:0)
2023-03-15 15:29:03 +0800
```
## module/folder name 盡量不要與 model or class name 相同
ex: Redis 曾經改成 RedisST
## 開發環境手動補上 db2 partition name.
rails console
```ruby
require "./scheduled_tasks/db2_monthly_partitioning_and_cleanup_task"
Db2MonthlyPartitioningAndCleanupTask.new.run
```
## 針對單一table 增加多個欄位時, 請記得是用 bulk: true
參考: db/migrate/20201229085120_add_columns_on_zuora_invoices.rb
```ruby
def change
change_table(:zuora_invoices, bulk: true) do |t|
t.column :tax_price, :integer, default: 0, null: true
t.column :tax_rate, :decimal, precision: 5, scale: 2, default: 0, null: true
t.column :zuora_sub_id, :string, default: '', null: true
t.column :zuora_contact_id, :string, default: '', null: true
end
add_column :zuora_users, :zuora_contact_id, :string, default: '', null: true
end
```
## [20230106] Rails 中常搞混的時區問題 Time.now vs Time.current
[Rails 中常搞混的時區問題](https://khiav223577.github.io/blog/2018/03/03/Rails-%E4%B8%AD%E5%B8%B8%E6%90%9E%E6%B7%B7%E7%9A%84%E6%99%82%E5%8D%80%E5%95%8F%E9%A1%8C-Time-now-vs-Time-current/)
二者代表的時間是一樣的,差別在於時區可能不一樣
* Time.now 使用當前機器作業系統的時區
* Time.current 使用 Rails 中設定的時區
```ruby
Time.current 去取代 Time.now
Time.current.utc 去取代 Time.now.utc
Time.current.to_date.to_s 可以取代常用的 yyyy-mm-dd 的格式.
# 常用的一些格式.
# 今天
Time.current.to_date
Time.zone.now
Time.zone.today
Date.current
# 昨天
Time.current.yesterday.to_date
Time.zone.yesterday
Date.current.yesterday
Date.yesterday
# 指定時間
Time.zone.at(1520015100).to_date
Time.zone.parse('2018-03-03 02:25:00').to_date
```
## Rescue 使用時機
1. 不想過程中去造成程序中斷. ex: 付款流程中, 更新資料到第三方.
2. 盡量避免程式中去用 xxx.split(',').uniq rescue nil , 因為錯誤就讓他噴, 這樣我們才有修正的機會.
3. rescue 其實速度不快. 所以在 api 層級不要用它來 render 40200, 應該直接判斷錯誤, 它只是用來捕捉非預期的錯誤.
4. 注意 rescue 內使用的變數, 否則可能在rescue 內會在噴 exception.
5. 該噴錯就噴錯不要隱藏, 否則會隱性債務越疊越高.
```ruby=
begin
...run code...
...run code...
data = xxx.method
...run code...
rescue => error
ExceptionMailer.email_exception_notification(
title: "Send exception mail",
body: data
).deliver_now
end
```
## Send exception email 內容?
請站在 debug 的人的角度來思考, 你需要哪些資訊?
1. 發生什麼問題? => e.message
2. 程式中哪裡發生? => e.backtrace
3. 何時發生? => mail 寄送時間.
4. 當時發生時的資料? => params 用於 reproduce 或是補資料用.
5. 額外資訊 => %x[hostname].gsub('\n', '') 發生主機來自哪裡.
範例:
```ruby=
Mails::Shoryuken::ExceptionMailerJob.send_mail(
mail_method: :email_exception_notification,
title: "[Render Commands Error] Generating Commands is failed. Host Name: #{%x[hostname].gsub('\n', '')}",
body: "exception: #{e.message}, backtrace: #{e.backtrace}, params: #{params}"
)
```
## ActiveRecord::Base.transaction 使用時機.
Transaction(交易)保證所有資料的操作都只有在成功的情況下才會寫入到資料庫,最著名的例子也就是銀行的帳戶交易,只有在帳戶提領金額及存入帳戶這兩個動作都成功的情況下才會將這筆操作寫入資料庫,否則在其中一個動作因為某些原因失敗的話就會放棄所有已做的操作將資料回復到交易前的狀態。在Rails中使用交易的方式像這樣:
```ruby=
ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end
```
所以要了解他的使用時機, 才不會誤用. 舊程式中可能有部分誤用, 所以之後碰到可以找人討論一下.
**只適合在用在整個資料串都必須一致與完整的情況, ex: 付款交易過程, 整個流程都需確保成功**
## [Rails4關於-update-method-的不同之處](https://blog.niclin.tw/2017/03/08/%E9%97%9C%E6%96%BC-update-method-%E7%9A%84%E4%B8%8D%E5%90%8C%E4%B9%8B%E8%99%95/)

使用下列三個都要注意
* update_column
* update_columns
* update_all
優點: 速度快等同於對db直接操作. 因為會 skip validation 與 callback.
缺點: 你要知道自己在做什麼與目的, 因為可能後續會產生 bugs, 因為後續人家加上的 callback 可能不會被執行到.
其他 methods 剛好優缺點相反.
之前在升級 rails4 的時候, policy 與 heartbeat api 有做 workarount 來提升 效能.
至於rails 5 與 6 是否有不同, 到時候再說.