惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
阮一峰的网络日志
阮一峰的网络日志
G
Google Developers Blog
J
Java Code Geeks
Blog — PlanetScale
Blog — PlanetScale
大猫的无限游戏
大猫的无限游戏
云风的 BLOG
云风的 BLOG
Vercel News
Vercel News
L
LangChain Blog
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
腾讯CDC
博客园_首页
博客园 - 聂微东
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
M
MIT News - Artificial intelligence
WordPress大学
WordPress大学
D
DataBreaches.Net
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗

Nic Lin's Blog

謝明真 - 高效領導力的課後筆記 NFT 開發實戰!基礎智能合約入門 (3) NFT 開發實戰!基礎智能合約入門 (2) NFT 開發實戰!基礎智能合約入門 (1) 如何自我檢測 log4j CVE 漏洞 Rails 如何在資料寫入時記錄來源 IP 位置 如何經營工程師 Youtube 頻道 - Part 8 營收篇 如何經營工程師 Youtube 頻道 - Part 7 酸民文化篇 如何經營工程師 Youtube 頻道 - Part 5 設備器材篇 如何經營工程師 Youtube 頻道 - Part 4 後製剪輯篇 如何經營工程師 Youtube 頻道 - Part 3 文案企劃篇 如何經營工程師 Youtube 頻道 - Part 2 設備器材篇 如何經營工程師 Youtube 頻道 - Part 1 制訂頻道方向篇 如何經營工程師 Youtube 頻道 - Part 0 Rails 中避免 race condition 的最佳實踐(二) Rails 中避免 race condition 的最佳實踐(一) 10 分鐘整合 google sheet 做自動化開發功能週報 經營 Side Project 300 天所帶來的收穫及挑戰 我的 Youtube 影片製作流程 API 設計時必須注意的 HTTP header 底線問題 如何提升你的程式可讀性之實務技巧(三) 如何提升你的程式可讀性之實務技巧(二) 如何提升你的程式可讀性之實務技巧(一) Ruby 中使用 freeze 優化效能的時機 避免 React 中的 useEffect 無限 render 在 Rails 內輕量使用 Vue Component 的最佳實踐 如何在區域網路用 Docker 架設有 SSL 的 Gitlab 從被問到問人,那些我常問的面試問題 [Rails] 如何漂亮寫出可維護的 query (Maintainable Rails Query) 在已知長度情況下優化 slice 的性能
Carrierwave 與 PaperTrail 的天生不合
Nic Lin · 2018-05-26 · via Nic Lin's Blog

前言

這兩支算是滿普遍熱門的 rails gem

  • 做 file upload 會用 Carrierwave
  • 紀錄 model operation log 會直接用 PaperTrail

但這兩支 Gem 有天生不合的地方,如果安裝時沒有稍微調整,等數據跑下去,發現時就會知道這是個巨坑

設定情況

  1. 一個常見情況 User table 有 image 欄位,資料格式是 string, 用來傳使用者頭像的數據
  2. 設定了 has_paper_trail, 只有在 update 時觸發的 callback 會記錄 object 前後變化
class User < ApplicationRecord
...
mount_uploader :image, HeadImageUploader
has_paper_trail on: [:update]
...
end

在另一張表 Order 裡面,我希望能夠拿到該訂單下訂時的 user 的 is_enterprise 狀態

class Order < ApplicationRecord
...
  def owner_is_enterprise_when_ordering?
    owner.versions.version_at(self.created_at).is_enterprise?
  end
...
end

因為 PaperTrail 再執行 version_at 時,會直接去下 WHERE 找出當時時間 range 的 Log 並把裡面的 object deserialized 出來成一個 instance,也就是執行了 reify

所以到這裡一切都很美好,只要 call owner_is_enterprise_when_ordering? 我可以知道當時下訂單時,該使用者的企業認證狀態囉, right?

踩雷的開始

但今天我就拿到一個 error

ActionView::Template::Error: undefined class/module HeadImageUploader::Uploader70109597847360

這時我就不明白怎麼會炸在 HeadImageUploader ?

直到我嘗試了更新 User 的 image 後,去執行 owner_is_enterprise_when_ordering? 發現確實炸了

/home/deploy/.rbenv/versions/2.4.4/lib/ruby/2.4.0/psych.rb:253 in load
/gems/paper_trail-8.0.1/lib/paper_trail/serializers/yaml.rb:17 in load
/gems/paper_trail-8.0.1/lib/paper_trail/version_concern.rb:184 in object_deserialized
/gems/paper_trail-8.0.1/lib/paper_trail/reifier.rb:17 in reify
/gems/paper_trail-8.0.1/lib/paper_trail/version_concern.rb:217 in reify
/gems/paper_trail-8.0.1/lib/paper_trail/record_trail.rb:412 in version_at
app/models/order.rb:391 in owner_is_enterprise_when_ordering?

嗯,Log 顯示一路炸上去

繼續追查下去,發現一個重點,通常 PaperTrail 預設的儲存格式是 YAML,但像是 Carrierwave 就是 JSON,也就是我們會遇到所謂「天生不合」的問題。

我們發現在每一次的 callback, paper trail versions 會將 image 欄位試圖存入整個 object, 然後在執行 reify 時在做 object deserialized 就炸了

接著開始找解決方案,大致有兩種作法

  1. skip 掉 image 的 paper trail
  2. 對 paper trail 做 monkey-patch

不過不管你挑哪一種作法,基本之前存的資料如果拉出來會爆就是廢了,畢竟他可是直接存 YAML 呢(竟然是預設值),然後你就會發現最好玩的是…

在 PaperTrail 的 issue 中,有人建議 A JSON serializer should be included by default

然後 Carrierwave 裡面的 wiki 也直接教你 How to: use with PaperTrail

結論

最後我是放棄 patching 資料了,就直接 rescue 找不到資料的提示了

提醒如果有這兩個 Gem 混用的時機,切記要注意

最好一開始就把 PaperTrail 預設改成 JSON 或是把 file uploader 的欄位 skip 掉

但在討論串中就有人認為既然都 Paper trail 了,理當是每個欄位都需要被紀錄,所以把欄位 skip 掉也不是很好的作法。

如果無藥可救,就放棄整個 PaperTrail 的 reify 功能吧,反正你也對爛掉的資料做不了 deserialize…