Tag coding
書摘 | Soft Skills = The software developer's life manual
公司前輩分享了 Soft Skills: The software developer’s life manual 這本書,後來又在 Soft & Share 看到推廣介紹,抱著當休閒讀物的心情來閱讀是很不錯的。 作者 John Z. Sonmez 在這本書中想要涵括的內容實在包山包海,如果對於人生迷惘的捧油,的確只要看了這本書所簡單介紹的內容就大概可以對人生重拾一些希望,也能循著這些主題繼續往深探索。比較特別的是,每個章節的篇幅被設計成大概一篇 Blog 文長度,所以閱讀起來的節奏感很是不錯,即使有些主題令人打哈欠,有些主題不斷地出現各種課程推銷,但都還能在睡著與失去耐心之前看完一個段落,這點是很值得效尤的閱讀設計技巧。 討論的主題分成七大部分: Section 1 - Career / Section 2 - Marketing Yourself 這兩部份討論的事情差不多,主要觀點是:把自己當成事業經營。 每個人的人生想成就的事物不盡相同,但大致上不出「創業」、「職員」、「自由工作者」這三種工作型態,每種工作型態都有其優缺點,沒有最好的選擇,只有最適合、最自在的選擇。制定目標的技巧:由大至小,先抓大方向,然後再去規劃要怎樣分階段性地往大方向前進,在執行計畫目標的過程當中,也不斷地去重新思考「目前的走向」是不是在往「想要的目標」前進,不斷地修正、不斷地思考,減少瞎忙的狀態。 每個人的人生想成就的事物不盡相同,但大致上不出「創業」、「職員」、「自由工作者」這三種工作型態,每種工作型態都有其優缺點,沒有最好的選擇,只有最適合、最自在的選擇。制定目標的技巧:由大至小,先抓大方向,然後再去規劃要怎樣分階段性地往大方向前進,在執行計畫目標的過程當中,也不斷地去重新思考「目前的走向」是不是在往「想要的目標」前進,不斷地修正、不斷地思考,減少瞎忙的狀態。 把自己當成事業經營,適當時候要勇於承擔風險,即使是受僱,心態上也應該將工作表現視為擦亮自己「工程師專業」這塊招牌的方法之一。心境不同,面對困難的態度也會有所不同。 除了砥礪自己的正直人格之外,也要適當地行銷自己的工作能力,名聲資產的累積管道除了被動地讓共事的同事發掘之外,可以積極地利用自媒體增加自身技術能力的曝光度,對公司內部分享技術心得、寫 blog 文章、參與 open source 專案、錄製 youtube 教學影片、參加 conference 或 meetup 活動,用開放地態度分享自己的心得,和業界的其他工程師教學相長營造正循環,累積一些口碑的被動收入(? Section 3 - Learning 作者提出了一套學習的方法論,大致上是: 了解領域範疇的大輪廓 制定學習目標 定義目標達成標準 搜集學習的資源(書、演講、課程) 制定學習計畫 過濾資源 學習可以開始動手的最小幅度知識 透過實際使用學習到的知識做一些有用的事情 第 8 步驟撞牆的時候回過頭去繼續補充所需要的進階知識 透過教學他人來融會貫通學習到的知識內容 個人覺得學習陌生領域最困難的入門點在於「不知道自己目前不知道什麼」、「不知道自己應該要先知道什麼」,所以步驟 1~6 其實已經有點「雞生蛋、蛋生雞」,有時候會遇到選一本很多人推薦的書開始讀了以後,陷入書裡面額外提到的各種細節而無法前進(其實大多時候是那種書都很厚重本身就有一種難以征服的心理壓力),又或者是努力地做了一堆「入門」練習然後就卡住無法自己融會貫通地應用在想做的專案上。 我後來發現一個簡單的方法,可以不用在學習之前就花時間搜集資料跟理解當下看再多也看不懂的材料,那就是 Udemy 的課程(或是其他已經整理好的學習計畫),特別是一些透過實作來做教學的程式課,選一些「看來像自己專案需要之技術」、「對於某特定技術的簡介與案例操演」類課程,跟著課程的模組實際動手做,不錯的課程設計已經把步驟 1~6 給完成,直接進入 7~8 階段。最後透過在自己的自媒體上分享所學到的知識內容,重新在腦中組織一次剛塞進去不久的知識,順便累積曝光資產,有時候遇到新的問題要回去查找資料時,自己寫的 blog 文章也會意外成為不錯的參考資料。
工具 | Kindle "My Clippings.txt" 的轉檔 gem KCFU
Kindle 的剪貼簿功能雖然在 Kindle 上很好用,但真的要抽出來另外處理書摘的時候還真的麻煩,因為 My Clippings.txt 就是一個很簡單的純文字檔,每次在 Kindle 上面 hightlight / bookmark / notes 時就會依照書的 title 去新增一段特定格式的文字段落,所以如果同時看很多本的時候,打開 My Clippings.txt 會發現裡面就依照 clippings 時序夾雜著來自不同書裡的內容,得再經過一些處理才能分成不同的來源。 原先有找到 firewood 這個套件,不過沒辦法在我的電腦上使用 QQ,乾脆就自己刻個輪子吧。 GitHub 上面的 kindleclippings 是用 Ruby 寫的,簡單又方便使用,站在小巨人的肩膀上刻了一個 kcfu 來做基本的拆分檔案,另外加配了一個陽春的 Markdown 格式轉換器方便做書摘。 最簡單的用法是開一個資料夾,把你的 My Clippings.txt 放到裡面,再寫個小 Ruby 檔: # kcfu.rb require 'kcfu' parser = Kcfu::FileUtil.new parser.parse_file(File.expand_path("#{File.dirname(__FILE__)}/My Clippings.txt"), convert: :markdown) 然後安裝 KCFU(沒有安裝 Ruby 的同學若要服用請先安裝 rbenv 跟 ruby ) gem install kcfu # Under folder of your kcfu.rb and 'My Clippings.
在 Trailblazer 的 Policy 中透過客制 Exception 來處理複雜的「錯誤回應」
為了盡量貼近 Trailblazer 的設計概念,許多原先會透過 Controller before_action 去處理的權限管控,盡量都搬進 Trailblazer 的 Policy 裡。 Policy 採用類似 Pundit 的語法,典型的 Policy 如下: class Thing::Policy def initialize(user, thing) @user, @thing = user, thing end def create? (admin? || approved?) && @thing.persisted? end private def admin? @user.admin == true end def approved? @user.is_approved end end 在 Operation 中若要調用這隻 Policy 的話要宣告: class Thing::Create < Trailblazer::Operation builds -> (params) { dispatched_class_accroding_to(params) } def self.dispatched_class_accroding_to(params) Thing::Create end include Trailblazer::Operation::Policy policy Thing::Policy, :create?
初探 Trailblazer 框架
最近公司的 Rails 專案試用了 Trailblazer 這套整理 Rails 程式碼的框架。(目前使用的是 1.1 版) Trailblazer 是擺在 Rails 上的一套工具,雖然有本專書可以翻找,但實際上就是個自成一格的整理程式碼套路,除了要學新的 DSL 外,在 API 文件沒有寫得非常詳細,加上使用者也沒多到可以 stackoverflow 的情況下,假如開發需求和設計者 Nick Sutterer 設想的情況不一樣時,小小撞牆是難免的。 Trailblazer 是模組化設計,所以並非所有模組需要安裝才能開始享受 Trailblazer 帶來的方便感,除了主要的 Operation / Policy / Contract / Cell 等稍微有摸過外,其他的眉眉角角尚待開發。稍微整理一下截至目前為止使用這套框架的個人認知,這套 Trailblazer 的目的是將原先 Rails 單純 MVC 架構下可能會散亂在 Controller / Model 層的商業邏輯,集中起來包在 Operation 的概念裡,(在理想狀況下)分別簡化日益肥大的 Controller / Model / View 邏輯: Controller 讓 controller 單純負責傳送 current_user / resource 到正確的 operation 並視操作結果導向相對應的路由 權限控管交給 Policy 處理,用 controller 塞給 operation 的 current_user 配合 operation 的 model!
筆記 | Practical Object-Oriented Design in Ruby (POODR)
Practical Object-Oriented Design in Ruby (POODR) 不會很厚,循序漸進地介紹物件導向設計的各種重要概念,而且範例用的是 Ruby 來解說,挺親切的。 除了各種設計原則之外,也簡要解釋了 Inheritance / Module / Composition 的使用時機與差異。 最後一章介紹測試原則,除了說明一般的測試原則之外(例如主要應測試 public interface / incoming message / outgoing command ),也很清楚地說明要怎樣去分別把不同的測試責任分在 module / test double / test class 上,以及讓 test double 與實際的程式碼同步的技巧,可以反覆閱讀的參考書。 Chapter 1 - Object-Oriented Design P.4 Practical design does not anticipate what will happen to your application, it merely accepts that something will and that, in the present, you cannot know what. It doesn’t guess the future; it preserves your options for accommodating the future.
利用 find_in_batches 在 Rails 做跨資料庫的資料移轉操作
在處理跨資料庫搬動並且需要一筆一筆資料去做各種運算處理時,最直覺的方式,是把表格裡的所有資料通通透過 ORM 撈到 ActiveRecord::Association 陣列裡,再用 each 去做操作: User.all.each do |user| user.email = "assign@new.email" ... end 但由於 Rails 會需要先去建立這些 objects,如果資料筆數很多的話,一次通通撈出來會佔用過多不必要的記憶體,可以使用 find_in_batches 做批次處理,減少系統 RAM 的負擔。 User.find_in_batches do |group| group.each do |user| user.email = "assign@new.email" ... end end 但如果要做跨資料庫的處理時,需要特別安插 ActiveRecord::Base.establish_connection 讓 Rails 知道當前操作需要連到哪個資料庫,舉例而言,假如想把資料一批一批從 :origin 調出來,然後檢查、編輯以後塞到 :migrated 資料庫裡,那麼需要在 Query 前去告訴 Rails 目前要連到哪個資料去撈資料: # Tell rails to get data from :origin ActiveRecord::Base.establish_connection :origin User.find_in_batches do |group| group.each do |user| # user variable here are the user object that Rails queried from :origin DB and initialed # Tell rails to switch connection to :migrated for data manipulation ActiveRecord::Base.
使用 Google Sheets API 拉資料到 Rails APP 裡
Google Sheets 很好用,一些程式中需要跨部門討論的字串定義,又或者是需要讓不方便存取原始碼的同事也能清楚知道原始碼裡部分設定字串的時候,通常都可以利用 Google Sheet 來做討論平台,再以 Google Sheets 上面的文件為基礎,匯入主程式中做各種處理,確保主程式的內容與 Google Sheets 上的內容同步。 要匯入 Google Sheets 內容到程式裡最簡單的方式,是輸出 csv 後寫個小 script 去讀取 csv 資料,轉成 Array 後就能靈活使用。 但若是開發過程當中常常要不斷更新 Google Sheets 欄位後再手動匯出 csv 跑 script 去後處理,這種半自動的方式還是令人覺得太過麻煩。可以考慮進一步用 Google Sheets API 去拉特定 Google Sheets 工作表資料,把半自動的「到瀏覽器視窗匯出 csv > 儲存檔案到特定資料夾 > 執行 script 完成匯入」簡化為「執行 script 完成匯入」。 首先到 Google API Console 申請一個可用的 API 身份,因為這隻 API 只要存取特定 Google Sheets 的內容,與其他 Google User 沒有互動,所以選用 Service Account 的授權方式就可以。 如果你的 Google 帳號之前沒申請過 Google API 的話,要先「建立專案」。
在 rails 上實作轉移 Parse 手機推播服務到 Amazon SNS
簡要說明 為了避免使用者因服務轉換而被迫強制更新,移轉過程採用漸進方式。 如果不需要如此痛苦的同時使用兩種服務的捧油,可以參考這一篇的實作,直接用 AWS Mobile Hub 的服務做批次資料移轉。 由於在轉換期需要同時支援兩邊服務運作,又要避免使用者重複收到相同的推播,需要實作「對同一裝置擇一服務進行推播」。本篇實作的概念是,使用者只有在用新版本的 APP 登入時,伺服器才會從 APP 取得 device_token 去註冊 Amazon SNS endpoint_arn,同時刪除 Parse 服務上的使用者的所有 Installation 紀錄。 Parse 跟 Amazon SNS 的 API 邏輯有些不同,主要差別在於,Parse 只需要在同一 request 裡就可以達到「推播給符合某條件的 channels 組合,並且可以指定其中的特定 users 組合」,但是 Amazon 的推播就是 topic_arn / target_arn 兩種,要收到 topic_arn 的話要先讓 endpoint_arn 訂閱特定的 topic_arn,而 target_arn 則是一個 request 只能推播給單一 target_arn (endpoint_arn)。 開發步驟 打包一隻 AmazonSnsWrapper 把 Amazon SDK 提供的 method 客製成符合需求的介面,專門負責跟 Amazon server 溝通。 新增 AmazonSnsInfo Model 以記錄使用者的 SNS 訂閱狀況。 新增 ParseInstallationWrapper 打包 Parse RESTful API 裡的 method,查詢使用者之前透過 APP 註冊的 Installations,並且刪除已經成功轉換到 Amazon SNS 的 Installation。 新增 update_push_subscription / logout API 來針對 APP 傳送的 device_token 做各種 push subscription / unsubscription 的操作。 新增 PushServicesWrapper 把所有 Push 行為的邏輯打包在一起方便做服務切換。 一、 AmazonSnsWrapper 首先,我們得先建好 Amazon SNS 服務中 endpoint_arn / topic_arn / subscription_arn 新增、查詢、訂閱與刪除等機制,我習慣把第三方 API 的互動包成一個 Ruby Class 方便測試跟管理,建一個 AmazonSnsWrapper 來打包 SDK 相關的程式碼。
Rails grape API 上實作 JWT 多重登入
多重登入的需求,在服務本身允許「跨裝置體驗」或者是「接受不同 request agent (APP request / mobile browser)」的時候顯得重要。 理想情況下,可以讓使用者在介面上面清楚知道目前帳號的登入狀態,並在登入數超過服務上限的時候,讓使用者自己選擇要移除登入授權的裝置,使用者流程大概會像(這篇文)那樣,要做到類似參考連結這樣的管理介面,需要我目前還不會的前端的配合建置與基本的 session 管理 API。本篇只先建置允許使用多重登入的資料結構與登入驗證 API。 選擇用 JWT (Jason Web Token) 作為主要的憑證溝通,優點有一些: 透過加密 payload 來比對資料庫內容的方式,可以不用在資料表中「直接存入」與憑證相關的訊息,加密跟解密只要透過協定就能進行調整。 payload 裡面塞入的訊息可以自訂,在各服務跟裝置之間交換加密過後的使用者資料。 signing key / protocol 可以在有資安疑慮時更換。 使用 Devise 的 Rails 捧油也可參考 devise_token_auth 這個 gem,整合效果在文件上面看起來很完善,但因為目前我手邊的系統絕大多數的 request 都來自 grape API endpoint ,比較過後,跟搞懂並駕馭 devise 設定比起來,我還是選擇自刻 JWT 較節省開發時間。 在 Rails grape API 上實作 JWT 多重登入,分 4 個階段。 建立 user_sessions 資料表跟 UserSession Model Setup,目前找得到的 Rails JWT 整合教學大多是用在 Controller / Rails View 上,為了之後如果要導入前端管理裝置時可以很方便地去刪除跟紀錄不要的 Session,我選擇了透過 UserSession 這個 model 來做媒介,之後要新增 API 的時候直接去對這個 table 做 query 跟各種欄位處理,盡量把 Session 管理跟目前龐大的 users table 切乾淨。 寫一隻自己的 JasonWebToken Service 把 jwt 的 code 包裝得更適合自己的系統使用:雖然 jwt 這隻 gem 的語法已經寫得很美,但還想更方便地只要把 headers 塞進去這個 wrapper 就可以得到 user object 回傳或者是指定的錯誤訊息。 做好資料結構跟 JasonWebToken Service 後,就可以開始使用準備好的工具來更換 Grape API 的登入跟檢查憑證機制,首先要在登入的 API output 提供 jwt 字串。 最後,在檢查憑證機制的 helper 裡使用 JasonWebToken class 去 decode request 裡面的 jwt。
Rails 中使用 Paperclip 存 URI 附件
使用 Rails APP + Paperclip Gem 做附件存取系統,靜態檔案的資料夾格式預設是跟著 id 跑( /000/000/000/ 九碼化的 ID 切成 3 階資料夾),最近因為要做資料庫合併,大批的資料會被賦予新的 ID,原先放在 AWS S3 上面的靜態資料夾結構就會 mapping 錯誤,必須要依照新的 ID 去存放相對應的資料夾結構。 由於部分圖片附件在存取時還會同步進行縮圖,如果要用 s3-cmd 這類的外部工具直接搬資料,得連各種不同 model 的縮圖定義一起處理,痛苦指數不低。 原先預計要從 HTTP GET request 的 response body 裡直接塞 tempfile : url = 'https://s3.amazonaws.com' path = '/BUCKET-NAME/MODEL/ATTACHMENT/000/016/222/original/FILE.jpg' conn = Faraday.new(:url => url) do |faraday| faraday.request :url_encoded faraday.response :logger faraday.adapter Faraday.default_adapter end response = conn.get path model.attachment = response.body 一直噴出各式各樣無法存入或者檔案格式錯誤的錯誤訊息。 試過自行重組檔案的檔頭 : attachment_file = { :filename => /^.