什麼時候該引入 Lockfile?

原文撰於 Twitter。 撰文初衷 第一次看到沒有 commit package-lock.json 的 repo TL;DR 事實上這樣在 library 上沒有什麼問題。Lockfile 的追蹤有個小原則: 應用程式建議追蹤 lockfile:不追蹤,下次 npm install 就無法確定具體的依賴版本是什麼。 函式庫可以不用追蹤,因為使用者安裝套件時,套件管理器會根據依賴自動選取最適合的版本,而你自己的 lockfile 會被忽略1。不過建議追蹤,見下文。 函式庫「該不該」追蹤 lockfile? 假如函式庫有用到諸如 ESLint 的工具,追蹤一下可以避免之後設定開發依賴的麻煩, 所以像 NPM 官方就是推薦 無論如何都追蹤 lockfile。 不過也有預設不推薦在 library 情境下追蹤 lock 的例子,比如 Rust 的 Cargo 套件管理器2。不過 Cargo 的開發工具主要都是作為 submodule 安裝在系統中,通常不會跟著 repo 一起追蹤,所以不太適合放在一起比較。 不過要注意:這時候的 lockfile 就不是追給下游應用程式看的,主要是為了自己開發方便。 為什麼「應用程式」就該追蹤 lockfile? package.json 通常不是描述固定的版本,而是一個版本區間:舉個例子:你可能在 package.json 裡面描述 vue: "^2.4.0",但實際上 NPM 幫你選了 2.7.13。這個行為是可以預測的,可以參考 NPM 官方的 Semver 計算機:https://semver.npmjs.com。 ...

October 18, 2022 · pan93412

The comparison of Linter and Formatter

Motivation src: https://twitter.com/bystartw/status/1568217424658583553?s=61&t=UVzhpuQaGcgZkGpySz_yVQ Noticing some developers are confused with “Linter” and “Formatter,” I made a simple figure showing what messages Linter and Formatter prefer to print. Thanks to @nonoesp for his incisive introduction to Linter and Formatter!

September 9, 2022 · pan93412

善用 Swift 的字串擷取功能簡化 I18n 流程

撰文原由 在 威注音 2.1.0 SP1 的更新日誌 下看到了這麼一段話: Interface Builder 給每個標籤的命名都是隨機的,手動改起來又低效又容易改壞,對多語言本地化而言簡直是天災。 事實上在 WWDC21 中,Apple 就為 Xcode 推出了一項可以解決這項痛點的新功能:「使用編譯器擷取字串」。與早期透過從 Swift 原始碼拉取可翻譯字串,經常漏掉一些字串的方式不同——這個功能會先編譯原始碼,然後從編譯出的檔案中判斷可以翻譯的字串。 啟用功能 這項功能在新專案是預設啟用的,但舊專案可以 opt-in:點開專案設定,進入 “Build Settings”,展開所有功能(All),尋找 “Swift” 然後找到 Localization 的 “Use Compiler to Extract Swift Strings”,將其設定為 Yes 即可。 匯出字串 開啟之後的國際化與本地化方式,跟先前會不太一樣。以往需要手動改 strings 檔案,現在可以先到 “Product” > “Export Localization…” 匯出編譯器擷取出的所有字串: 接下來指定匯出的路徑,然後等待 Swift 完成編譯並擷取字串。若是從舊專案 opt-in,擷取字串的過程中可能會拋出一些錯誤,這個時候就得修正(如果只是警告的話也可以忽略): 翻譯流程 接下來就可以進入選擇好的資料夾,使用 Xcode 點開對應語系的 xcloc 檔案: ...

August 27, 2022 · pan93412

文科生也能懂的 Rust async 机制

背景 https://twitter.com/repsiace/status/1554103778994900992/ 修改一下:work stealing, thread-per-core, waker, mpsc, task queue 只有他们懂… 正常人不可能看懂 – @twicemoemoe, 22-08-02 作为一个文科生,其实觉得 async 真的没有想象中的这么困难 ⋯⋯ 😂 或许搭配一些图片会好懂很多吧。 TL;DR 不废话版本 Sync(同步):一件事情做完之后,再做下一件事情。 blocking(堵塞):指“等一件事情”的行为。 Async(异步):一件事情还没完成,可以做其他不冲突的事情。 concurrency(并发):程序 架构 中,各个任务可以 独立运行 的特性。 future:Rust 中的一个异步任务的表示。 polling:不停地询问任务,确认事情是否已经完成。 event-driven:事情完成后,任务自己发通知表明完成。 parallelism(并行):同时 运行 数个程序的行为。 thread(线程、线程):系统进程(任务集)的基本单元。thread 通常是交由 CPU 内核运行。 spawn(生成):指产生 thread 的行为。 thread pool(线程池):将 thread 高效分配给每个任务的地方。 Async runtime: 以 tokio 为例 join (macro):并发运行 async 函数,并在全部完成后回传。 select (macro):哪个 async 函数快,回传那个 async 函数的结果。 main (attribute macro):在 main() 初始化 runtime。 block_on:在 sync 上运行 async 函数。 spawn:并行运行 async 函数。 spawn_blocking:在异步函数里面,为一个高耗时且同步 (blocking) 的函数另辟新线程 (thread)。 同步 (Synchronous) 跟异步 (Asynchoronous) “同步”就是整个程序等一件事情完成(blocking,堵塞)。“异步”则是一件事情还没完成,可以做其他不冲突的事情。 ...

August 7, 2022 · pan93412

文組也能懂的 Rust async 機制

背景 https://twitter.com/repsiace/status/1554103778994900992/ 修改一下:work stealing, thread-per-core, waker, mpsc, task queue 只有他们懂… 正常人不可能看懂 – @twicemoemoe, 22-08-02 作為一個文組,其實覺得 async 真的沒有想像中的這麼困難 ⋯⋯ 😂 或許搭配一些圖片會好懂很多吧。 TL;DR 不廢話版本 Sync(同步):一件事情做完之後,再做下一件事情。 blocking(堵塞):指「等一件事情」的行為。 Async(非同步):一件事情還沒完成,可以做其他不衝突的事情。 concurrency(並行、併發):程式 架構 中,各個任務可以 獨立執行 的特性。 future:Rust 中的一個非同步任務的表示。 polling:不停地詢問任務,確認事情是否已經完成。 event-driven:事情完成後,任務自己發通知表明完成。 parallelism(平行):同時 執行 數個程式的行為。 thread(執行緒、線程):系統處理程式(任務集)的基本單元。thread 通常是交由 CPU 核心執行。 spawn(生成):指產生 thread 的行為。 thread pool(執行緒池):將 thread 高效分配給每個任務的地方。 Async runtime: 以 tokio 為例 join (macro):並行執行 async 函數,並在全部完成後回傳。 select (macro):哪個 async 函數快,回傳那個 async 函數的結果。 main (attribute macro):在 main() 初始化 runtime。 block_on:在 sync 上執行 async 函數。 spawn:平行執行 async 函數。 spawn_blocking:在非同步函數裡面,為一個高耗時且同步 (blocking) 的函數另闢新執行緒 (thread)。 同步 (Synchronous) 跟非同步 (Asynchoronous) 「同步」就是整個程式等一件事情完成(blocking,堵塞)。「非同步」則是一件事情還沒完成,可以做其他不衝突的事情。 ...

August 7, 2022 · pan93412

Rust 的 crate/super/self 關係

假設 rust_hello_world 的目錄架構長這樣:(範例源自於我手邊的某個 production 專案) rust_hello_world Cargo.toml src cli opt.rs lib.rs cli.rs logger.rs 「crate」概括來說就是一個專案,用 Cargo.toml 區分。每個 mod 都是一個層級,super 就是上個層級,self 就是本層級。上面的圖已經把 crate / super / self 的對應關係寫得很清楚了,以下寫範例: cli::opt::Opt 想要讀取 LoggingLevel,路徑可以這樣走: super::super::logger::LoggerLevel crate::logger::LoggerLevel cli::opt::Opt 想要讀取 PROG_NAME,路徑可以這樣走: super::PROG_NAME crate::cli::PROG_NAME 事實上 Rust 的模組關係也沒這麼複雜。把上面的例子變成目錄: [crate] / [mod] cli/ [mod] opt/ [struct] Opt.txt [const &str] PROG_NAME.txt [[pub] mod] logger/ [[pub] enum] LoggingLevel.txt super 等價於 ..,self 等價於 .,而 crate 則類似 /。

October 13, 2021 · pan93412

VBA 筆記:選取事件

有天我突然想寫一個 Excel 按鈕,但是又不想要做一個 Form,而想到最好的辦法,就是按 Excel 中的格子。那該怎麼做,才可以做出這種偽按鈕呢? 原理思考 Excel 中,按一下儲存格代表? 就是選取「單一」儲存格。就跟你拖曳選取一堆儲存格、選取整整一行或一列所代表的含意是相同的。 那怎麼知道使用者選取了什麼? 用 VBA 的事件 (Event)。只要使用者做了什麼動作(觸發),Excel 都會通知你發生新事件,告訴你使用者做了什麼。 首先,Excel 遇到新事件時,會先看看你有沒有在指定的地方放下動作(Sub,子程式),如果沒放就等同忽略,而有放就會觸發(Trigger)這個動作。所以我們只要知道「選取儲存格後該在哪裡接收事件」,就完成了。 正文 建立接收事件的子程式 VBA 中有一個跟儲存格變更有關的事件,叫做 Worksheet_SelectionChange,長得像這樣: Private Sub Worksheet_SelectionChange(ByVal Target As Range) 別急著複製。VBA 其實有提供一個非常簡單的事件選取工具。首先開啟 VBA 編輯器,找到你想要監聽的工作表,然後按一下上方的 [(一般)],之後選擇 [Worksheet]。 選擇 Worksheet 然後選擇 [SelectionChange],搞定! 選擇 SelectionChange 了解子程式的結構 首先,我們來看看產生的程式碼。 Private Sub Worksheet_SelectionChange(ByVal Target As Range) End Sub 你會發現到裡面出現了非常多奇怪的東西。什麼是 Private 和 ByVal?Target 是什麼?As Range 又是什麼東西? 首先,每個工作表都有自己獨立的事件,也就是你在 A 工作表選取東西,不干 B 工作表的事。如果用 Public,就代表 A 工作表的事件也會影響其他工作表。這不應該發生。而 Private 就可以限定這個事件是 A 工作表 only 的。 而 ByVal 則需要一點資料結構的知識。VBA 傳遞參數有兩種形式,一個是 ByVal,建立物件的副本,另一個則是 ByRef,傳遞指向原物件的參考。有興趣可以看看〈VBA 中 ByVal 和 ByRef 的基础用法和区别〉。 ...

October 18, 2020 · pan93412