文章列表

練手的膠 - 1| HG EXIA能天使

手機‧電玩
MacauYeah・2025-03-24

之前筆者一直在分享膠模制作上的選擇,這期筆者就來分享一下自己踩過的坑,也許我們可以當作一個系列來分享。筆者參考一位內地up主,分享低價位的橂型,挑選適合練手的素材。不過筆者也只是一名新手,只可以從一些素組、打磨、補色方面,為大家導覽某些套件。筆者也不會限制於低價位的素材,只要有做過,也適合練手的,就會分享。有興趣新入坑的朋友,可以趁著再販期,一起入手筆者的舊套件,一起來體驗一步一步制作的過程。 第一期要介紹的,是2007年的推出的HG EXIA。Gundam.info的Youtube 頻道在2025年初,免費播放OO動畫,為這年的OO系列再販商品帶熱潮,筆者也想當然地趁著這個機會補票入貨。EXIA現在1/144比例中,可以通販買到的,就RG15、HGOO 01、HGOO 44,這三款。因為早期RG軟骨的問題,就沒有考慮RG,只有選擇HG。不過該HGOO 01是2007年的商品,真的不是一般的樸素。除了套件中原有的補色貼紙外,還需要進行多處補色。所以我們除了需要準備滲線、打磨工具外,還需要上色。不過筆者是簡化派,盡可能只用Marker筆補色,就結果來講,效果還不錯。 筆者就直接附上照片,指出這些補色的位置。 補色主要是天藍、深灰、淺灰、紅、黃、白 詳細圖片請見筆者IG 或者小紅書 https://www.instagram.com/p/DHfyseAvP8z/?igsh=MTc4Y3c5d29ydnJwOQ== http://xhslink.com/a/6d6BFOdGRzr8 留意手部前臂,後期滲線有機會流入透明件的地方。要先滲線再補色,最後才上透明件。 補充,條件許可的話,最後還是需要噴上保護漆,因為筆者用的是水性Marker筆,極易刮漆,多層保護漆,後期拿上手,壓力少一點。

重入膠坑6-補色地獄

手機‧電玩
MacauYeah・2025-03-23

之前筆者就有介紹過水性馬克筆補色、滲線,對於有一直砌開最新HG、MG的朋友來講,只需要考慮滲線就夠。但對於一些便宜價位的入門級的HG或SDEX模型,補色就更重要,因為它們的成型色大都只有兩至三種,即使套件中有提供補色貼紙,亦無法函蓋所有部位。筆者最近做的一款舊HG能天使及SDEX巴巴托期天狼座就是如些。 你所需要的是一套足夠便宜的平替 之前筆者亦介紹過【迪斯派】的模型專用的水性馬克筆,但對於這麼大量的補色,迪斯派的單價也是相當讓人心痛。最近筆者就發現到另一款更便宜的平替品,【多樂繪直液式丙烯馬克筆】。筆者寫稿當天,非金屬色中,也是45.8RMB 24色,72.8RMB 48色。相對於6.9一支的迪斯派非金屬色,多樂繪很便宜,顏色選擇也很多。 多樂繪亦提供散裝購買,7.9RMB 自選三支非金屬色,即是2.64一支。如果大家不想一次過全部購買24色,可以參考筆者以下型號 配合區部重塗: 600(白色), 603(黃色), 608(藍色), 622(淺灰), 680(深灰), 664(紅色)。上述與萬代的成型色還是會有色差,但相對不太明顯。其中600(白色), 603(黃色),遮蓋力較差,需要多次重塗以便發色。 還有一些筆者用到但不是通用色,628(天藍色?),642(海軍藍?) 使用效果: 筆者在塗裝部份只是處於基本補色要求,沒有試過混色、疊色、過渡等高階用法。對比迪斯派,多樂繪的感覺真的差不多。 操作 使用前先搖一搖筆身,拔蓋就用。 上色前需要打磨嗎? 對於白色、黃色等,先打磨模型表面,有助加強附著力。但白色始終難發色,也要多次重塗。深色的不用打磨表現也很好。 易刮漆嗎? 易刮,所以要留意邊角位。完成補色後記得上保護漆,上保護漆之前也記得再檢查一遍。 遇到的最大問題 多樂繪的黑色出墨過快,難以控制影響範圍,因為顏色太深,事後也很難清潔。但其他顏色未有出墨過快的問題,未知是否個別事件。另外筆者亦未試過傳統的水性消色筆,都一律以酒精或牙籤清理錯處,暫時無需使用專用消色筆(多樂繪可能也沒有消色筆)。 迪斯派比多樂繪做得更好的可能是出墨的部份,它不需要搖筆身,也有正常的顏色表現。但迪斯派的顏色選擇很少,灰色、藍色與萬代的成型色很不協調,小部份補色也很顯眼。筆者認為它最大的問題是缺少深灰色,這是萬代很多內構的常用色,再加上多樂繪價錢便宜一大截,一口氣買幾次回來粗用回本。 如果大家有發現一些更細微的分別,歡迎隨時留言交留。

Docker 中的非管理員用户 Docker non-root user

科技新知
MacauYeah・2025-03-14

Container USER為何重要 在制作Docker Image的過程中,有時會接觸到 USER 這個設定。這事關到最後的 Docker Container內部運行的那個 user 到底會有什麼權限。大家也要知道,Docker Container 其實也只是一個 Linux 上的程序,也就是如果Container內權限過大,也有機會從 Container 內部存取到 Host上的資料。 一般情況下,Docker Image 預設的 USER 就是 root,最基礎的base image都是一樣。而我們想換,其實也相當簡單,就像Linux上起User一樣,只要經指令RUN adduser xxx 或RUN useradd xxx 也可以在 Docker Image 中創建帳號和 home 資料夾,之後就隨時經USER xxx來切換 實際上是不是這麼簡單? 如果你將要Container中執行的程序,是一個binary,平常你在Linux中也是以 non-root 方式執行,那麼是的,就是那麼簡單。例如你執行系統中的java, node, python,原本在Linux中就已經是誰都可以,那麼你的docker container 也應該沒有難度。 但如果原本的安裝包,預設是由system service來啟動,我們就要花點力氣,看看那個service是怎樣呼叫binary的,然後就一步一步模擬它的做法。例如筆者有打包的codeserver,預設是system service啟動,但它也有提共binary的執行方法,安定好home資料夾後,我們也可以手動啟動。 泛生之檔案權限問題 上述binary的情境之所以簡單,是因為大部份情況下,我們都只對於container 內部運行考慮即可,因為預設投產情況下的運作模式,都是隨時起、隨時刪、隨時砍掉重練,只要container內部運作可以自給自足,就可以了。Docker Swarm的運作也是如此,所以它不預期有的持久化資料權限的問題。 而持久化資料權限的問題,其實早在單個Linux伺服器就已經存在。同一個伺服器中,不同process就有不同的UID,當他們需要共同讀/寫某些檔案,就會設定多人權限。同理,當多個Container要共同檔案,也是同樣問題。在討論共享檔案之前,我們先看看預設 Docker Storage Mount 會給我們什麼權限。 如果是bind mount,bind mount的權限預設會是Host內的檔案或者資料夾的權限。 如果Host是root,container內是non-root,container有機會無法讀寫bind mount內的檔案。 留意權限設置就可以解決問題 如果Host是non-root,但container 內是root,從container內生成的檔案,Host的non-root user就無法使用。 Host是non-root的話就一定無解,Host至少有sudo權限,臨時變成管理員,去修正問題。 如果host和container也是non-root,但UID不夾,其實也不能交換使用。 跟上述一樣,最後要靠sudo來解決問題。 如果host和container也是root,就沒有權限問題,但就有安全性的風險。 如果是volume mount,就還是看看 mount path 是docker image layer中現有的 path還是新起的path 大部份手動建立的named volume都是root 經docker compose起的named volume滿足以下條件的話,將會是non-root。 docker image 中的已有該path存在。 named volume未存在,docker compose會把對應path的內容在初次建立時抄到named volume 中。 例如ubuntu:24.04中的/home/ubuntu,存在於docker image中,它的擁有者就是UID 1000,我們經docker compose HOME_VOLUME:/home/ubuntu,在HOME_VOLUME建立時,就會是UID 1000。但如果是 NOT_EXISTS:/home/ubuntu/somethingNotExists,那麼NOT_EXISTS建立時,也會是root 上述討論的Storage mount是集中在單機情況下,使用HOST OS的本地儲存。若現在的場境是多機共享的share storage,就會更麻煩,還要看看那個share storage本身的屬性。例如常見的Linux NFS,其實有指定的權限,跟NFS的Login權限有關,如果你的process本身對檔案權限很敏感,就請先不要挑戰NFS(例如postgresql)。 Rootless mode - Rootless 模式 Rootless 模式指的是在Host中,執行Container的使用者,不需要是管理員,筆者就常用於開發環境中。投產環境中反而沒有聽過這樣的討論,因為投產環境很少可以讓非管理員去執行這麼重要的環境管理。 雖然只是開發環境,但這像前述的bind mount討論中,如果Host是non-root,但container 內是root,又或是兩者non-root,但UID不夾,也會出現權限問題。無腦的將host user加入docker group,只可以讓非管理員可以運行docker,但解決不了權限問題。 真正有條件解決的,可能就會向linux subgroup的方式發展。暫時筆者用得比較順的rootless mode,可以無腦用的,不是docker,是podman。有興趣的朋友可以經podman官網看看教學,它給筆者的感覺就像是自動轉換UID。 podman rootless mode 想看更多 筆者已經將過去的文章重新整理成gitbook,有興趣睇更多的讀者,可以來筆者的gitbook再翻一翻 https://macauyeah.github.io/AProgrammerPrepares/

Spring Boot 08 - 多情境設置 maven profile 與 application.properties 進階篇

科技新知
MacauYeah・2025-03-11

上期我們介紹完最直觀的用法,這期我們再來討論多管齊下的方向。 在開始之前,筆者總結一下上期的 Profile 的要點。 Spring boot 是經過 spring.profiles.active 去選擇什麼 (spring boot) Profile 生效 spring.profiles.active 它可以在runtime(運行時)動態更改 maven 是經過 xml 去選擇編譯時的 (maven) profile maven 編譯時為 spring.profiles.active 填入一個固定值 另外,筆者亦在測試途中,發現一個現像。 maven 並不提供混合 profile,即使下指令同時觸發兩個 profile ,最後亦只有一個 maven profile 生效。但這個部份筆者未在官方文件中找到,大家如果有任何發現,可以幫忙修正。 Spring boot 混合 Profile 當我們經IDE編譯時,可以為 spring.profiles.active 填入多個值,各值之間用逗號分隔,就可以觸發多個 profile 。 spring.profiles.active=dev,uat 程式碼中的application.properties, application-dev.properties, application-uat.properties 都會生效 Spring boot會先後載入上述三個檔案,如果有重複值,後面出現的會覆蓋前面的值。 spring.profiles.active如果填入的值與現在的application-xxx.properties不匹配,該部份不生效,例如 spring.profiles.active=dev,uat 程式碼中只有application.properties, application-dev.properties,但沒有application-uat.properties Spring boot會先後載入上述兩個檔案 上述的都好理解,當大家都接受上面的結論後,再來看這個現像。 spring.profiles.active 是啟動spring boot時,作為選擇profile的依據。 application.properties可以有一個預設的spring.profiles.active,正常跑spring boot就會看它。 正常跑spring boot時,還可以通過傳入參數--spring.profiles.active=xx,改變那個值。 Spring boot test 因為結構特殊,它只會看到 application.properties 中的那個spring.profiles.active值。 Spring boot test 暫時沒有方法傳入參數spring.profiles.active,但可以經程式碼 @ActiveProfiles 硬改運行中的 profile 。spring.profiles.active亦只會顯示 application.properties中的那個值。 Spring boot 混合 Profile 例子 大家看完概念之後,可以來看看實際例子。 當什麼都不加,就是根據application.properties的spring.profiles.active來啟動profile。 mvn clean compile spring-boot:run # or mvn clean compile package java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar 正常spring-boot:run的情況下,可以經的 --spring.profiles.active 覆蓋過application.properties內的值。 mvn clean compile spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=dev --spring.profiles.active=uat" mvn clean compile spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=dev,uat" # or mvn clean compile package java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev --spring.profiles.active=uat java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev,uat 上述例子,若dev,uat內的值沒有衝突,沒有覆蓋問題。但如果有衝突,最後會是uat內定義的值。 Spring boot test Profile 例子 因為不是正常spring-boot:run,所以那些參數都沒有用,具體只會看application.properties內預設spring.profiles.active mvn clean compile test -Dspring-boot.run.arguments="--spring.profiles.active=dev,uat" # arguments will be ignored, same as mvn clean compile test Maven Profile 例子 加入Maven之後,就可以修改application.properties內的預設spring.profiles.active。但要注意,maven只會有單profile 假設pom.xml如下 application.properties如下 spring.profiles.active=@active.profile@ 下述三組例子,有且只有uat生效。因為maven的uat生效後,會修改 mvn clean compile spring-boot:run -Puat # or mvn clean compile package -Pdev -Dci=true java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar # or mvn clean compile test -Puat 當然,你想要弄一個maven mix profile 也可以 以下例子可以令 dev, uat 同時出現在spring.profiles.active mvn clean compile spring-boot:run -Pmix # or mvn clean compile package -Pmix java -jar target/spring-boot-profile-0.0.1-SNAPSHOT.jar # or mvn clean compile test -Pmix Maven Profile Spring boot test例子 上述例子都了解後,最後就來看看全部混合的情況 當Test case中沒有硬改 profile 定義,application.properties中的spring.profiles.active就直接作用。以下情況就是同時運行dev,uat // java @SpringBootTest class ProfileTests { } // bash mvn clean compile test -Pmix 當Test case中有定義@ActiveProfiles ,application.properties中的spring.profiles.active的值會保留,但不在該test case中生效。以下情況就是同時運行uat,dev,但讀取spring.profiles.active的值會是dev,uat。 // java @SpringBootTest @ActiveProfiles(value = { "uat", "dev" }) class MultipleProfileUatDevTests { } // bash mvn clean compile test -Pmix 如果我們把maven 指令中的加入package,預期 test 執行的是 uat,dev 。而 jar 的打包結果會是 dev,uat。 // java @SpringBootTest @ActiveProfiles(value = { "uat", "dev" }) class MultipleProfileUatDevTests { } // bash mvn clean compile test package -Pmix 但請盡量不要這些做,因為會越來越混亂,特別是打包 prod 環境。為減少出錯的機會,例如test污染了prod的環境,筆者在package時,通常都會跳過test。 mvn clean compile package -Pprod -Dmaven.test.skip=true

Spring Boot 08 - 多情境設置 maven profile 與 application.properties

科技新知
MacauYeah・2025-02-25

為何要有不同的建構 Profile Profile這一字,很難在IT技術文章中翻譯,它在Spring boot中的語意大概就是一個設定一個固定的運行環境參數合。例如我們做開發時,有些只想在開發環境中出現的設定,諸如測試用的資料庫、細緻一點的LOG層級,都寫在dev profile中。當換成正式環境時,我們也有一套全新的配置,而且會集中寫在prod profile中。把這些參數設定從程式碼邏輯中抽離,可以讓你的程式碼簡潔很多,也方便對比不同環境的設定。 application.properties Spring Boot (Spring Boot Starter) 就提供了 Profile 管理。我們可以為一個Spring Boot 模組設定多個不同的 application.properties src/main/resources/application.properties 為預設 (default profile) src/main/resources/application-uat.properties 為驗收環境專用 src/main/resources/application-prod.properties 為投產環境專用 src/main/resources/application-test.properties 為自動測試專用 在執行程式時,我們只要動改變啟動的參數spring.profiles.active,例如 mvn spring-boot:run -Dspring-boot.run.arguments="--spring.profiles.active=uat" # or mvn package && java -jar target/YOUR_JAR_NAME --spring.profiles.active=uat Spring Boot 就會指定載入 application-uat.properties 的內容,如果有些值沒有定義,它會再追溯到預設的 application.properties中。 在運行中改變啟動參數的情況可能不多,筆者更常用的情況是在編譯期間產生多個 Jar 檔,不同 Jar 檔指定不同的環境,方便系統管理員取用測試。想做到這個效果,我們需要在 application.properties 中,我們還需要加入一句spring.profiles.active=@active.profile@,並在編譯工具中加入這個變量,例如筆者常用的 maven pom.xml 中,就會有這一串設定 它在 maven clean compile package 時,就已經可以在JAR中填入固定spring.profiles.active。那麼每次執行時,都會是指定的profile。 mvn package -Puat java -jar target/YOUR_JAR_NAME 在這個例子中,JAR 中的 spring.profiles.active 就會固定是uat,我們不需要在啟動參數中加入字眼。 如果大家不會碰到混合Profile的話,其實上述的資訊已經足夠大家應付很多情境。 但當大家有追求,需要寫自動測試,有機會不同自動測試需要啟用不同的 Profile ,更有可能出現混合Profile的情況,這件事就變得很複雜。我們需要繼續深入了解一下 Spring Boot 的覆蓋機制,下面將會以測試方式導出結論。 如果真的對混合 Profile 沒有太多信心,我們也可以用單一 Profile 重組不同 properties 的方式,自行去模擬混合 Profile ,例如除了dev, uat, test之外,我們可以加入 dev-test, uat-test, default-test 作為驅分。這樣應該可以簡化測試的複雜度,不過 properties 檔案就可能會成幾何級成長。 但在某情特殊情況下,我們不可能簡單地重組 properties 等型式去做測試,例如針對部份uat-test的測試,只有部份可以執行,部份不可以,那麼我們還是需要用到混合 Profile ,限定某些測試需要執個某個 profile ,但其餘部份可以動態切換。 有條件的讀者,也可以先行試玩一下混合 profile 的特性,下期筆者再為不同情況作解紹。 混合Profile Source code spring boot profile

重入膠坑5 | 堆積變山積,山積有辦法解決嗎?

手機‧電玩
MacauYeah・2025-02-20

針對大標題的問題,先講結論,堆積是沒法完全避免的,但我們應該有條件防止堆積變成山積(大量堆積)。 堆積不可避免的原因 以前筆者一直是玩遊戲多,面對特價節日買多了的現像,直到PS4後期,都還會有這個問題出現。但在過渡到PS5早期,就不再有這個問題。因為數位遊戲必定是越賣越便宜,除了機器壞、停產的問題,基本上不會有所謂的錯過好遊戲的問題。不同平台,每年都有節日特價,急玩頂多也就原價買入,能等就一定會有好價錢。 但模型可不一樣,實體商品都有限量的問題,首發一定貴,再販之後會回落到正常價,但再過一期就不一定有現貨。所以大家的策略一般是先買入自己認為不錯的新貨/特價品,以免後來買不到,即使山積也會照買。不過問題就是當年紀越來越大,又或是對制作越來越有要求,作業的速度就會越來越慢,然後山積就會越來越嚴重。 改變一下心態 首先這裏不討論成品玩具的山積問題,因為成品玩具至少可以折價出讓,只要夠捨得,想清貨一般都有市場。這裏講的山積問題主要是針對拼裝模型部份,針對那些要自己出手,慢慢一件件加工的玩具。 最初,我們可能很想完美地砌完一台模型,打磨、上色、擺拍。但因為一些技術問題,例如因為損毀要另行修復、制作難度、天氣問題、家庭問題,令大家的進度一直無法向前。久而久之,就會更無心情去完成作品,食之無味,棄之可惜。 面對這個問題,我們可以考慮放過自己,其實不一定要完美制作才能體現樂趣。素組,AA膠強行修復,遮醜,拍攝區部特寫,也可以一定程度的避重就輕地跳過有問題的部份。 貴精不貴多 由少量堆積變成山積主要的原因源自於【不想錯過】,但對於長遠目標來講,山積可能意義不大。 如果我們的目標是收藏,我們會想精制,而面對可能損毀的風險,雙入更具意義。所以不同時期山積多款模型,就變得沒有對沖意義。 如果我們的目標是重複把玩,我們可能更在意空間收納的方便性。若山積的話對於怎樣找模型,絕對是一個阻礙。舊模可能需要搬去一個更隱閉的地方,但這樣亦代表不再把玩,所以堆積也沒有價值。 重新定義如何斷捨離 回到本文最初的結論,堆積,或者叫作團貨,是沒法完全避免的。主要因為商品可取得性問題,再加上對珍品的不捨,一定量的堆積是必定會出現的。但我們可知道,物品始終都會老化或損壞,亦即是有保質期的問題,堆積不代表可以永久擁有。有些我們無法完成、修復的作品,就只好換個形式儲存,例如變成改件作為下一個作品的配件使用,又或是送出給下一位有緣人。新品購入時,亦要評估保質時間,自己是否可以在指定時間內開封體驗,免得錯過最佳觀賞時間。

Github copliot vs Intellij IDEA ultimate

科技新知
MacauYeah・2025-02-18

github copliot 最近正式開放每月限量免費使用,只要有github 帳號,就可以經過vscode copliot plugin,向 github copliot 交互式生成程式碼,又或是經 copliot 提供 code completion。大家會不會想過,加了github copliot的vscode,是不是效率暴升,可以跟傳統的付費IDE 例如intellij 的IDEA ultimate版本平起平坐? 流暢度明顯提高 是的,在生成程式碼方面,特別是code completion,在開啟copilot之後,實在好太多了。筆者長期寫java,vscode 原生的 java code completion,實在太陽春。java class name都很長,而且是強型別,很多時候都要完整打出class name。但大部份時候,筆者都要打很多個字之後,vscode才猜到你想打什麼,再給你可能的code completion,但這樣一來你也快打完了,幫助不大。要麼就是自己複制貼上,要麼就自己全拼出來。 在開了github copilot之後,在空行開始時,它就會開始猜你的意途,在打幾個字母以後,它雖然會頓一頓,但總在筆者跳去其他部份複制class name之前,就給出更新結果,實在省心太多。但猜測始終是猜測,大部份時候還是邊打邊修正。不過code completion方面,已經是追得上intellij,有些時候更是超越了intellij。例如我們有時寫 javascript 時,需要做多語言顯示,我們需要為每個語言設定一份i18n的翻譯。copilot 在這方面也能幫到忙,它會自動推薦可能的翻譯,你連問都不用問,這些功能,都不是 intellij 的本地運算會有的支援。 另一個要提提的是 copilot chat,它跟大家平時使用 chatgpt 程式碼生成的方面類似,只是它可以直接在vscode的某個檔裏直接交換生成程式碼。不過生成的品質都很一般,很初階的事可以做,深一點的就不懂。例如你很常寫java,但突然要寫javascript,有些javascript的array操作你懶得查,這時你可以叫copilot chat幫你生成。但若果你今日使用 javascript 框架,有一些 vuejs 或 reactjs 的結構參數傳遞你不太了解,你想找copilot chat,那就幫助不太。它依然可以生成一些程式碼,但對你碰釘的地方沒有修正意義。你還是需要自行從官方文件較對、研究Stack overflow中相似問題的解決方案。就跟chatgpt差不多。當然這些不是傳統IDE可以給你的。但如果現今對比的是收費的copilot chat和本地免費的ollama qwen2.5-coder,copilot chat就沒有太大性價比。 可以作為付費IDE的平替嗎? 如果我們拿vscode + github copilot 跟 intellij IDEA ultimate來比較,前者入門價錢是120美元一年,後者入門則是169美元(次年續期有優惠,但到了第三年才會比 github copilot便宜)。單看價錢的話,github copilot的確比較便宜。想省點錢,github copliot絕對是一個可以考慮的選擇。但除了錢以外,或者我們還要考慮一些其他因素。 公司立場上,介不介意你的IDE上傳資料到cloud service上面? 付費IDE的除錯功能、多環境整合、程式碼品質分析,這些關係到長期維護,非程式碼生成部份,是不是可以忽略不計。 筆者在開發開源的程式,長期都使用vscdoe,在配上 github copilot 後,明顯覺得它提升了 vscode 的流暢度。但相對實際工作上,筆者還是會集中使用 intellij IDEA ultimate 。因為即使vscode 有明顯改善,但日常碰到的問題更多不是在生成部份,而是解決那些似是而非的程式碼結構陷阱,這方面還是intellij 更幫到忙。(當然stack overflow和其他網路資源才是真正的救命靈藥。)

重入膠坑4 | 走線滲線的選擇

手機‧電玩
MacauYeah・2025-02-14

重入膠坑將近大半年,筆者陸逐實驗了過去不敢做的東西。前幾次就分享了剪鉗、打磨、補色的心得,時代真的一直在變,很多東西已經不需要大師級就可以動手做,而且有一定美觀效果。 例如薄刃剪+打磨+消光漆,已經一定程度可以淡化水口。用不著全上色,也就意味著普通人做個後樓梯勇士噴噴消光就可以了,其餘都是室內一般環境可以操作。當然打磨部份可能要加水,才能減少對健康的影響。 又例如補色方面,很多水性馬克筆可以選擇,軟筆頭可以大面積筆塗,很多品牌還是無味無臭,家裏隨時可以使用。相較以前動不動要用溶劑開油、洗筆,不方便且很大異味,水性馬克筆可謂極其方便。 這次筆者再來分享一下滲線和加深刻線的心得,希望退坑很久的淺度用戶們,有機會都可以試試這些簡單的操作,美化自己過去的作品。 2B鉛筆是神物 在十多年前,看教學,一定會教滲線筆和滲線液。滲線筆應該是那時候最易勾劃出立體感的工具。滲線筆其實是一些很幼細的油性馬克筆,但不論筆頭多幼細,走線時都嫌太粗了。所以筆者過去很長時間,都不滲線。但慢慢地,有網友提出了使用鉛筆勾線,一來隨手可得,二來勾錯也可以很容易擦了重來。 筆者試用過,大推這種做法。不過有兩點要注意的 筆頭要夠幼,走線勾線時才不會勾在坑的兩邊。平時寫字用的0.5是太粗了,那怕用畫面專用的0.3也是太粗了(也貴)。但其實我們可以自行手動磨尖筆頭,這樣才能上色到坑中間。不過磨尖了,代表筆頭很易斷,力度要很溫柔。 馬克筆上過色或上過消光漆的地方更易使用2B勾線。筆者未知確切原因,但感覺上是因為漆面不再原始膠面那麼光滑,質感更像A4紙張,2B筆跡更易留在模型表面。 加深刻線再滲線 在那個古早的過去,滲線液也是自行經法瑯漆稀釋調配。但現在就已經有現成的滲線液可以即開即用。某牌子的滲線液,其實也只是法瑯漆預先稀釋的成品,筆者也先針對這類滲線液做介紹。 這類滲線液是利用管線的毛細現象達到滲線效果,但為確保滲線液的流動性,模型原來的坑線需要先加深,而且在沒有消光漆的情況下使用。(如已使用消光,要重新噴光油,讓表面回復平滑,滲線後再重新消光)。這些都是教科書般的操作。 筆者想分享的,主要是刻線及滲線時機的部份。通常教科書是上色後再滲線,所以一般是假組、打磨、加深刻線、上色、滲線、保護漆(光油/消光)。但若我們只是素組補色,筆者就試行假組、補色、加深刻線、滲線、打磨、再補色、保護漆。 第一次的補色主要為大面積補色,補完後就進行刻線滲線動作,打磨要留在刻線之後,主要是因為刻線容錯率很低,不一小心就刻出界,打磨可以一定程度修補刻錯的地方。真的不行,就再少量補色。打磨過程也可能引起刮漆問題,再補色是必需要。 第一次大面積補色,若為淺色系列,可以用2B鉛筆直接走線勾線,不需要刻,減低出錯風險,劃出界也可以再補色。 水性馬克筆滲線 之前一直有講,水性油易被刮漆,但我們也可以反過來利用這點,直接用水性筆走線勾線。在沒有上色的部份,我們可以直接刻線,再用水性馬克筆走線。走完之後跟滲線液有少許差別,我們需要趁著水性油完全完乾之前,就用紙巾擦走多坑線周圍的部份。(但如果本身有上色,就只能回到2B鉛筆及滲線液的方向,這樣才不會出現擦走底色的問題。) 這個做法的好處是,我們顏色的選擇多了,也沒有法瑯漆X-20溶劑爆膠的風險。刻線部份依然會有出界問題,處理方式同前述一樣。 以下就是筆者運用上面所講的方式的實驗作品,右半是2B勾線及水性馬克筆滲線混合使用的效果,左半則是完全沒有塗裝/補色的狀態。

學習寫程式,除了複制貼上還有什麼?

科技新知
MacauYeah・2025-02-07

不知道大家是如何學習特定程式語言/框架的建構? 也不知道大家可如何保持程式庫/框架的最新狀態? 筆者就分享一下最新的經驗,看看對大家有沒有得著。 制作自己的範本 跟著程式/框架的導覽教學(Tutorial)走一偏 從零起一個新專案 設定專案,該用的基本功能全部設定好,作為概念驗證(Proof of Concept),也作為日後範本(Template)之用。 有需要用新專案,就複制之前的範本,再逐一修改名字或路徑的設定。 上述做法,是筆者過去比較常用的策略。面對很統一要求的專案,都有效。當程式庫有更新,我們可以選擇只局部修改,範本就可以長期用。我們也不需要經常從零走一篇。 練手的Code - 從零起一個新專案 上述的範本做法,對於現時需求多變的專案,可能不是很有效。例如有些專案使用Session Auth,有些則是Api Auth,有些則是Open Auth。同一個範本中有齊多種Auth的設定,原本難度就有夠高,之後複制完還要自行禁用不相關的部份,也是相當的煩人。當範本中多有個地方都有互相衝突的地方,複制範本就不是一個很易的做法。 面對那些複雜的配對,我們務必要真正了解技術的運作原理,然後為每個功能都從零建一個專案,做一個最簡單的Proof of Concept。重點不是在未來拿它們複制貼上,而是用來厘清概念,哪段程式對這個功能至關重要,哪段其實沒有作用。 如果可以,每次程式庫/框架升級時,都從零建一次。這樣一來可以練手,加深記憶,二來是每次版本的變動,有些程式碼可能已經變得沒有作用,原本的寫法並不再是最簡的。當然這個也可以為每個功能獨立做成範本,到有需要的時候再抄少量的程式碼就好。 其實練手的過程中,我們亦會慢慢熟習IDE的功能,有些IDE或Plugin已經很方便地自行完成一些設定。所以筆者漸漸的也習慣了不抄程式碼,改為以IDE Plugin的方式建立,某些真的很不熟練的部份才會維持範本複制的型式。 這是筆者最近學習vue3 的練習清單,還在持續新增中。讀者們有興趣也可以一起來修訂。 https://github.com/macauyeah/AProgrammerPrepares/blob/main/src/vuejs/TimeAttack.md

Spring Data Jpa 自動化的選擇 - Code First

科技新知
MacauYeah・2025-01-22

Code First vs Database First 在早期SQL資料庫盛行的年代,在設計要使用資料庫儲存資料時,很經常遇到一個策略選擇的問題*Code First* vs *Database First* 這兩個策略的差異可能越來越講不清,筆者也找了一些現時網路上的講法。 Code First: 先從寫程式的角度出發,設計數據模型,再使用工具把你程式碼中的數據模型類(Class),生成一個對應用SQL資料庫的表(Table),自動編做好對應的數據結構(Schema)。這樣你在設計時,以程式設計為主導,方便熟悉程式的人使用。這常見於第一手開發設計,因為資料都是第一次收集和儲存,考慮收集程式的運作最為實際。 Database First: 先從SQL資料庫的儲存、取用資料的方式出發,先用SQL成生Table及Schema,再轉變成為程式碼中的數據模型。這樣的資料庫在日後作分析用途時,比較簡單易懂,方便使用熟悉SQL的人去使用。這也常見於二次開發程式,因為這樣可以確保不會錯誤地破壞原有資料庫。 那麼筆者為何講這兩個差異越來越講不清?那是因為現在的資料庫不能單純地只考慮初次或二次開發問題,而是需要考慮多個系統協調運行的問題。 多系統共享協定 - Database First 因為隨著資料系統發展,有些資料會作為數據源出現或用作共享媒界,如果一定要對設計策略作分類,在多系統協調運作下,這些應該叫使Database First。不論它們是SQL還是NoSQL資料庫,我們的程式碼都要為這個預先定義好的數據結構作出妥協。不論使用工具,還是人為分析,都要把共享的數據結構轉換成自己程式中的數據模型。 即使不是多系統協調運作,有時候因為要移植系統,但同時又要令兩個系統版本相容。新系統也是被逼使用Database First的方式設計。 自動化考量 - Code First 前述我們講到,很多時候我們也是從Database First的方式思考。不過筆者就這個Database First,也弄到滿身傷痕。 首先,拋開工具轉換的誤差,我們人為的把共享數據轉化為數據模型,共享數據有時會有一些先天的缺陷,例如: 資料沒有設計Primay Key (主鍵,唯一鍵)、日期時間的定義不明確等。面對一些意義不明的數據來源,要整合確實很要命。而且二次開發中,不可能100%重用原有的資料庫結構,很多時都會加入新的欄位或更多表格去計數。一旦加入新欄位,在團隊多人開發中,那麼使用唯一的共享開發環境,就變很易有程式碼上的衝突。 若需要多人開發,各人有一個Code First的開發用資料庫,是很必要的。這也可以在系統正式升級前,對比開發中資料庫及舊資料庫的結構,觀看它們之間的差異,評估升級的風險。 也許Code First並不是重點,重點是可以隨時建立一個測試用的資料庫,這才方便合作開發。自動化的地方,不單只限於數據結構,範例資料也該是如此。如果有維繫一個初始範例資料,可以在有需要時自動生成,對於多變的環境一定有很幫助。 現時,筆者基本上都會人為檢視資料庫,人工對照編寫程式中的資料結構(即是人工的Database First),並確保那時程式再次經自動化生成的測試用資料庫,並沒有失真(即是Code First)。至於範例資料,初期筆者也只使用SQL生成,但後期因為資料結構開始複雜,筆者也暫暫使用程式碼生成,雖然工作量會多了,但對於資料庫升級、品牌更換,這是很有效的手段,程式碼升級測試也更順暢,絕比SQL生成更易維護。 Ref - Code First vs Database First https://builtin.com/articles/code-first-vs-database-first-approach

快速做用 elasticsearch 做中文 n-gram 關鍵字全文搜尋

科技新知
MacauYeah・2025-01-16

有些時候,我們對一些文章資料,光是使用Ctrl-F文字區配搜尋,很難找到完全吻合的結果。這時候,我們可以試試看快速搭建自己的中文搜尋引擎,看看能不能更易地找到資料。而中文搜尋引擎,其實用免費的elasticsearch也可以做到。我們就來看看怎樣快速起lab吧。 經 docker 下載及運行 elasticsearch docker run -p 127.0.0.1:9200:9200 -d --name elasticsearch \ -e "discovery.type=single-node" \ -e "xpack.security.enabled=false" \ -e "xpack.license.self_generated.type=basic" \ -v "elasticsearch-data:/usr/share/elasticsearch/data" \ docker.elastic.co/elasticsearch/elasticsearch:8.17.0 建立資料庫。在elasticsearch 中,示作index,並建立自己的n-gram analyzer和tokenizer。 curl -X PUT "localhost:9200/book-ngram?pretty" -H 'Content-Type: application/json' -d' { "settings": { "index" : { "max_ngram_diff" : 4 }, "analysis": { "analyzer": { "my_analyzer": { "tokenizer": "my_tokenizer" } }, "tokenizer": { "my_tokenizer": { "type": "ngram", "min_gram": 1, "max_gram": 5, "token_chars": [ "letter", "digit" ] } } } } } ' 假設資料庫每筆記錄有 record_id,title 和 content 三個欄位,其title, content都是中文內容。它們都套用 n-gram analyzer 。 curl -X PUT "localhost:9200/book-ngram/_mapping?pretty" -H 'Content-Type: application/json' -d' { "properties": { "title": { "type": "text", "analyzer": "my_analyzer", "fields": { "keyword": { "type": "keyword" } } }, "content": { "type": "text", "analyzer": "my_analyzer", "fields": { "keyword": { "type": "keyword" } } }, "record_id" : { "type" : "text", "fields" : { "keyword" : { "type" : "keyword" } } } } } ' 批量上傳內容。(如果要上載json檔,請把 -d'xxx' 改為 --data-binary @FILENAME) curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d' { "index" : { "_index" : "book-ngram" } } {"record_id":"1","title":"紅樓夢","content":"甄士隱夢幻識通靈賈雨村風塵懷閨秀"} { "index" : { "_index" : "book-ngram" } } {"record_id":"2","title":"西遊記","content":"混沌未分天地亂,茫茫渺渺無人見。自從盤古破鴻蒙,開闢從茲清濁辨。覆載群生仰至仁,發明萬物皆成善。"} { "index" : { "_index" : "book-ngram" } } {"record_id":"3","title":"水滸傳","content":"張天師祈禳瘟疫洪太尉誤走妖魔"} ' 多欄位搜尋,並指定title的權重為content的兩倍。 curl -X GET "localhost:9200/book-ngram/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "multi_match": { "query" : "開天闢地", "fields": ["title^2", "content"], "analyzer": "my_analyzer" } } } '

為程所困-是什麼讓你不想寫自動化測試?

科技新知
MacauYeah・2025-01-08

測試場 VS 自動化測試 筆者一直地更新自己過去所編寫的程式,很恐怖的是,那時的自己很少思考過怎樣寫測試Test Case。致使每次做更新時,都膽戰心驚,要手動建立測試場,人肉去測試每個可能有受影響的地方。在那些年的時候,有能力自己搭建測試場,已經是萬幸。但當面對一些要長期維護的程式,測試場的人肉測試並不是一個有效的方法,一來費時間,二來人腦記憶並不可靠。單靠自己去想想那些地方受影響,再測試,某程度是在挑戰人腦的記憶上限。如果是團隊合作,就更麻煩,你以為修改不會影響到其他人,結果卻是翻天覆地。 所以為求長治久安,編寫自動化測試,是有必要的。這些自動化測試,都算是回歸測試,每次程式有任何地方改動,都確保所有自動化測試被通過。理想始終是理想,但實際操作又會遇到怎麼的問題? 以筆者剛更新的程式為例,難以測試主要是當初沒有想過要測試這件事,所以程式結構通常是【連續順序】地執行。想分段測試?除非先重構。 Function中太多自己創建的Object 回顧自己的程式,初期編寫時,總會我手寫我心,每想要創建任何資源,在java中就會使用 new 字眼,或是自行呼叫某些 builder 類來取得資源,這是其中一個令自己無法寫測試的原因。 我們要想想,這些資源,是不是自己Function中所關心的核心。如果這個資源是被直接回傳的,我們要保留,如果它是HttpClient,只是要來獲取其他資源的媒介,我們或許可以利用依賴注入來取得它,即是把 HttpClient 改為經呼叫方傳入。注入的好處時,我們可以在Test中,修改那些資源的行為和結果。更進一步的是,把那些資源改為 interface 的方式存取,那麼在 Test 中就能更任意地控制該資源的行為。 首次重構某些資源成為依賴注入,大部份都會影響呼叫方,很多地方都要重寫。不論使用constructor injection, setter injection, annotation injection 等,上傳呼叫方,或多或少都會要加減改變參數。極致地,我們把構建都交給Program 框架去做,例如Spring Boot中,各種資源,都交給框架去自動配致。當然,這種做法的學習成本高,除錯成本也高。 【注入】其實是想在控作那些資源,在測試中運行得到固定的行為。使用前述的HttpClient例子,當我們業務邏輯是先訪問外部Web API,再根據結果做處理,那麼我們測試時,就會想模擬Web Api的結果。如果要做到自動化測試,最強硬的手段,就修改自己的HttpClient,模疑給出固定結果。 想要做到這種,在傳統的Java中,我們需要透過進一步抽離Interface去做。但這樣做很累,所以筆者通常會用如Mockito的程式庫,去修改HttpClient的行為。有興趣直接看程式碼的讀者,可以去看 github 。 當然,上述的 HttpClient 例子,使用測試場也有可做測試,自己再去模擬那些Web Api的回傳,有些情況下,這樣會更真實,但大家就必需好好定義測試場的行為。因為測試場可能與團隊的其他成員所共用,有機會其他人可能想要更多互動的測試方式,而非固定的結果。但並上非固定結果的測試場,自動化要測試的可控度就減少。

免費自用的私人AI助理 | Ollama - 本地大型語言模型

科技新知
MacauYeah・2025-01-06

不知道在澳門的朋友,有多少可以正常接觸openai?因為地方政策問題,像openai這種國外的大型語言模型(下稱LLM),澳門區都沒法接觸到。但隨著時間過去,即使我們不能直接接觸到算力很強的收費AI,我們只要有電腦,也可以佈署一些開源版本的LLM。只要我們可以安裝到ollama這套本地運算軟件就好 ollama是一個giuthub上的開源工具,讓用戶能夠在自己的電腦上運行各種大型語言模型(LLM)。基本上只要電腦是普通的桌上型windows, linux, mac,都可以運行它。下以面就介紹一下筆者的安裝經驗。 windows windows ollama windows 本地安裝ollama,真的很簡單,就是直接去官網下載就好 - https://ollama.com/download/windows 安裝完成後,在windows cmd再加一個基本的模型就可以了 ollama pull llama3.2 之後就可以開始跟llama問問題 ollama run llama3.2 windows openwebui 如果大家不習慣windows cmd的醜醜介面,想經過瀏覽器存取,我們可以再加裝openwebui。但這個必需要經第三方python或docker安裝。openwebui github指引 - https://github.com/open-webui/open-webui 經python pip install open-webui open-webui serve 經docker docker run -d -p 8080:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-webui:main 最後,打開browser,訪問 http://localhost:8080,openwebui就會要求大家先設立管理員帳號。 就那麼簡單,大家就有一個真正的私人AI助理。 steamdeck steamdeck 因為很多linux功能都有被限制,所以筆者就直接使用 podman 安裝 git clone https://github.com/macauyeah/ollama-steamdeck-podman.git cd ollama-steamdeck-podman podman compose -f podman-compose.yaml up -d podman exec -it ollama ollama pull llama3.2 同樣地,打開browser,訪問 http://localhost:8080就可以了,因為這個版本已有預設的管理員帳號,立即打開就可以使用了。 Ollama的開源模型 上文中一直提及 llama3.2 其實是 Meta 公司的開源模型,因為它的參數相對少,算力要求較低,可以在沒有GPU的環境下執行。若然大家算力足夠,可以使用其他模型,詳見 https://ollama.com/library 。見到合心水的模型,大家可以經 pull 指令下載。例如:小紅書的網紅們很多都推薦qwen2,我們可以 ollama pull qwen2 備註: openwebui 及 ollama 並不直接支援自己建立自己的資料庫。我們需要其他工具去補完,但筆者觀看各種教學,自己建資料庫的效果都不太好,所以暫時不做任何教學。 只要我們一直經ollama pull,就可以更新語言模型。但如果大家追求即時的網絡最新資料,大家可以看看LLM RAG的相關文章。但筆者亦未有成功的案例,有更新會另作教學。 opewebui並不是PDF閱讀器,但它可以預覽PDF中的文本,我們需要手動複制PDF中的文件後,才能經ollama分析文件內容。 若想切換模型,在指令介面中,我們多開一個分頁就可以了。若經openwebui,則可以在每句對話之前,經左上方選擇不同模型。

Steam OS 內建 podman DNS 問題解決方法

科技新知
MacauYeah・2024-12-20

前幾天筆者在介紹SteamDeck 內建的podman時,沒有測試得很清楚。在長期使用下,的確有些問題需要進一步處理,這裏就補充一下解決方法。 我們前一篇介紹的 Steam Deck 內建 podman ,配上再自行安裝 podman-compose 有時會出現warning :`WARN[0002] aardvark-dns binary not found, container dns will not be enabled`。這不單影響到沒有在 service 之間自動産生 DNS 記錄,還會令互聯網功能失效,因為它會是整個 DNS 解釋功能丢失了,只是在 service 中定義 DNS 的地址並不會解決問題。筆者亦測試過,照著原本的 docker 思路,使用最傳統的做法,自己起 network ,自己起 container ,然後再串連在一起,依然會出現問題:`Error: "slirp4netns" is not supported: invalid network mode`。所以根源問題應該不在 podman-compose 上,而是在內建的 podman 依賴上。 緊急的解決方案,我們需要用到 "network_mode: host" 的方式去解決。例如以下例子 "network_mode: host" 的主要作用,就是讓 container 直接在主機的網段上執行。上面的例子中的 postgres 資料庫,它預設使用5432端口,我們並不需要再獨立宣告,即使在 container 內外,都可以直接使用 localhost:5432 溝通。而使用了主機的網段後,DNS 也可以正常運作。但這個做法的缺點就是 container 內的所有 port 都自動佔用了 host 上的使用,有時候那怕我們並不需要,它也會被暴露在外。更可能的是增加了不同 container 之間的 port 衝突。不過筆者要用於開發環境,所以這並不會是很太大的問題。