跳到主要内容

· 阅读需 3 分钟

因为苹果出了 vision pro,看着我实在是想要体验一下。

其实我最想要的就是躺在床上网上冲浪,或者敲代码。这种头戴设备,然后好像可以提供超级多屏幕的方案就让我很想要。

那没办法,贫穷,我只有找了现在便宜的平替 Meta Quest 3 来试一试

体验

刚开始在国内过年的时候拿到的设备,在家里科学上网没办法成功激活,加上过年时间安排比较多,所以就一直放在那里了。现在到了新加坡,重新连上广域网,开始研究这个 quest 3 怎么用。

结论是还不错,差不多达到了我的期望值

游戏

讲游戏的人有很多,我就不讲了,节奏光剑确实不错

生产力

哈哈,我想要聊得多是这个东西其实主要有两个软件,我觉得让Quest3能够达到我的需求. 一个是 immersed, 一个是 fluid。

immersed

这个东西就是很不错啦,可以理解为电脑的外接屏幕,而且是很大的那种。

你可以在空间的任何地方放置它们,比如我躺在床上用,也是看着三个大屏幕,感觉还是很不错的。

我深度使用了几天,感觉清晰度还是不够,和物理屏幕比起来还是有点差距的。但是可用。

fluid

这个算是现阶段 Quest3 自带的浏览器的增强版,强在他的屏幕布局是更加自由的,而且清晰度拉满的,如果 immersed 能够达到这种清晰度,我觉得就很不错了。

这个软件的手部跟踪已经操作也做得非常自然(intuitive)了,是个不错的东西。

总结

就是还不错,花接近4000RMB体验空间计算的产品,而且对比起来,我感觉这是一个我买来不会吃灰的产品。

有点想转 VR 开发程序员了,哈哈哈,说不定是未来的方向呢。

特别是在用完 softspace 这个软件之后,感觉未来真的就是每个人带个便携式的 VR 眼睛,走在路上到处都是屏幕和广告

· 阅读需 1 分钟

最近好多项目都是 js/ts 生态的前后端,感觉要补习一下了,这一堆框架有点傻傻分不清楚。

image

了解了之后,打算将我用 golang 写的 undercontrol 换成这一套生态体系的实现

现在想法是现有逻辑迁移到 nest.js

· 阅读需 2 分钟

区别

  • 单引号('): 用于表示字符串文字。当您需要在SQL查询中包含字符串值时,您通常会将其放在单引号中。
  • 双引号("): 双引号主要用于标识数据库对象的标识符,如表名、列名、索引名等。使用双引号可以让您创建具有特殊字符或空格的标识符,或者与SQL关键字相同的标识符。

Demo

  1. create a strange table
CREATE TABLE "MyTable" (
"Column1 with space" integer,
"Column2-with-dash" varchar(50),
"Column#3" date,
"Column$4" boolean,
"Column%5" numeric(10,2)
);
  1. insert some data
INSERT INTO "MyTable" ("Column1 with space", "Column2-with-dash", "Column#3", "Column$4", "Column%5")
VALUES (1, 'Value1', '2024-02-03', true, 100.50);

INSERT INTO "MyTable" ("Column1 with space", "Column2-with-dash", "Column#3", "Column$4", "Column%5")
VALUES (2, 'Value2', '2024-02-04', false, 200.75);
  1. query the data
SELECT * FROM MyTable;
  1. alter table
ALTER TABLE "MyTable"
ADD COLUMN "order" integer;
  1. query the data
SELECT order FROM MyTable; -- error
SELECT "order" FROM MyTable; -- correct

· 阅读需 1 分钟

UTM 是一个功能齐全的系统模拟器和虚拟机主机,适用于 iOS 和 macOS。它基于 QEMU。简而言之,它允许您在 Mac、iPhone 和 iPad 上运行 Windows、Linux 等。更多信息请访问 https://getutm.app/https://mac.getutm.app/。

Snapshot

今天想在虚拟机上搞点骚操作,但是又觉得搞坏了从头重新弄一个太费时间了,就去看看怎么操作 snapshot,发现好像咩有直接很用户友好的GUI上实现这个功能

但是幸好 可以通过命令 直接达到效果,也够我使用了

# create an snapshot
$ qemu-img snapshot F89035CE-DEF9-49D7-97F8-7EC0C2F3F9D9.qcow2 -c snapshot1

# list out all the snapshot
$ qemu-img snapshot F89035CE-DEF9-49D7-97F8-7EC0C2F3F9D9.qcow2 -l

More info: https://github.com/utmapp/UTM/issues/5484

· 阅读需 2 分钟

You can find the runnable code at this github repo

You can find the video tutorial at bilibili

Mock Static Method

@Nested
inner class MockStaticMethod {
private val now = LocalDateTime.of(2023, 3, 3, 0 ,0)

@Test
fun `should return mocked datetime`() {
mockkStatic(LocalDateTime::class)
every { LocalDateTime.now() } returns now

println(LocalDateTime.now()) // 2023-03-03T00:00

unmockkAll()
}
}

Mock Extension Function

class MockExtensionFunction(private val lintao: Lintao) {
fun bar() = lintao.extensionFunc()
}

class Lintao

fun Lintao.extensionFunc() = "ExtensionFunc"

class MockExtensionMethod {
private val lintaoMock = mockk<Lintao>()
private val target = MockExtensionFunction(lintaoMock)

@Test
fun test() {
mockkStatic(Lintao::extensionFunc) {
every { lintaoMock.extensionFunc() } returns "mocked function"

val result = target.bar()

assertThat(result).isEqualTo("mocked function")
}
}
}

Mock Companion object

class MockCompanion {
fun bar() = LintaoCompanion.foo()
}

class LintaoCompanion {
companion object {
fun foo() = "Haha"
}
}

class MockCompanionTest {
private val target = MockCompanion()

@Test
fun `should return mocked value`() {
println(target.bar()) // Haha
mockkObject(LintaoCompanion)
// mockkObject(LintaoCompanion::class) 这种写法是错误的,不能加 `::class`
every { LintaoCompanion.foo() } returns "mocked value"

val bar = target.bar()

assertThat(bar).isEqualTo("mocked value")
}
}

AndThen

class VerifySecondCall(private val dependency: MyDependency) {
// there're two calls of function bar
fun foo(): String {
return dependency.bar("arg1") + dependency.bar("arg2")

}
}

class MyDependency {
fun bar(arg1: String) = "Before mock: Call with $arg1"
}

class TestDrive() {
@Test
fun `some test`() {
val mockDependency = mockk<MyDependency>()
every { mockDependency.bar(any()) } returns "mock1" andThen "mock2"

val result = VerifySecondCall(mockDependency).foo()

assertThat(result).isEqualTo("mock1" + "mock2")
verify {
mockDependency.bar(withArg { assertThat(it).isEqualTo("arg1") })
mockDependency.bar(withArg { assertThat(it).isEqualTo("arg2") })
}
}
}

Spyk

class UseSpky() {
fun foo(): String {
throw RuntimeException("Foo")
}

fun anotherFoo(): String {
return "anotherFoo"
}
}

class TestDrive1 {

@Test
fun `use spyk by normal constructor`() {
val useSpky = spyk(UseSpky())
every { useSpky.foo() } returns "mock"

val fooResult = useSpky.foo()
// this method returns the mocked value, didn't throw the error, which means the real implementation didn't run at all.
assertThat(fooResult).isEqualTo("mock")

val anotherFooResult = useSpky.anotherFoo()
// this method returns the real value,
assertThat(anotherFooResult).isEqualTo("anotherFoo")
}
}

· 阅读需 2 分钟

安装与配置

https://github.com/junegunn/fzf?tab=readme-ov-file#installation

点进去这个连接,repo 里面写得很清楚了

基础使用方法

https://github.com/LintaoAmons/easy-commands.nvim 下面所有命令都是在这个仓库下进行的 如果你想跟着敲一遍,可以 clone 到本地动手试试

直接敲 fzf

fzf

实际上执行的是

find * -type f | fzf

上个命令的结果交给 fzf 进行搜索

搜索文档内容

cat CommandUsecase.md | fzf

有点类似 interactive 版本的 rg

rg -N '##' CommandUsecase.md

搜索本文件夹内容

ls . | fzf

将 fzf 的结果用在别的命令上

Command expansion

打开 fzf 找到的结果

open $(fzf)

将 fzf 找到的结果放到 clipboard 下次随时粘贴

fzf | tr -d '\n' | tee >(pbcopy)

选择多个结果

fzf --multi --bind='ctrl-a:select-all'
cat CommandUsecase.md | fzf --multi --bind='ctrl-a:select-all' 
cat CommandUsecase.md | fzf --layout=reverse --multi --bind='ctrl-a:select-all' | pbcopy

实用搜索Tips

查看帮助文档

fzf -h | fzf

大小写敏感

cat CommandUsecase.md | fzf +i

严格匹配

在需要搜索的内容前面加个 ',就是告诉fzf,严格匹配这个pattern

And

空格

Or

|

以XX开头

^

以XX结尾

$

取反

!

THX:https://thevaluable.dev/practical-guide-fzf-example/

· 阅读需 5 分钟

我不长的工作经验内,接触的历史项目,怎么说呢,可能是各种历史原因导致代码可能不那么好维护(客气),其中有一种 Pattern 让我看到就头疼,那就是嵌套好多好多层的容器类型。

比如, Map<String, Map<String, List<String>>> 这样的复杂嵌套结构,我知道你是一堆有层次结构的String,但我有限的大脑容量,真是没办法时刻维护每一层String的真实含义,这种难以阅读、理解和维护的代码,我会放弃维护....或者,我一定要维护(工作嘛),我可能会用下面的方法稍微重构一下,只是为了理解代码的时候更加轻松点(降低我大脑的内存消耗)。

其实这种Pattern,我重构的目标就是去掉这种嵌套的结构,用 data class 来显式地展出他的上下文。那重构的步骤就是 从内到外 一步步构建 data class

例子分析

假设我们有这样一个数据结构 Map<String, Map<String, List<String>>>, 我真不想他出现在我需要维护的代码仓库里面, 他太模糊,比如,下面两个例子都是这样的数据结构,但是他们表达的含义完全不一样

例子 1: 员工和项目信息

  • 第一个 String 表示员工的姓名。
  • 第二个 Map<String, List<String>> 表示员工参与的每个项目及其详细信息。

重构方法

1. 内层建模: 首先,我们定义 Project 类,它包含项目名称和相关细节。

class Project {
private String name;
// 其他项目相关的属性
}

2. 中间层建模: 然后,我们创建 Employee 类,包含员工姓名和他们参与的项目列表。

class Employee {
private String name;
private List<Project> projects;
}

3. 外层建模: 最后,定义 Company 类,用于表示整个公司的员工信息。

class Company {
private Map<String, Employee> employees;
}

例子 2: 用户和社交媒体帖子

再来看另一个例子,同样的结构 Map<String, Map<String, List<String>>> 用于表示用户在不同社交媒体平台上的帖子。这里:

  • 第一个 String 表示用户的用户名。
  • 第二个 Map<String, List<String>> 表示用户在不同平台上的帖子。

重构方法

1. 内层建模: 首先,定义 Post 类来表示一个帖子。

class Post {
private String content;
// 其他帖子相关的属性
}

2. 中间层建模: 接着,创建 SocialMedia 类,它包含平台名称和帖子列表。

class SocialMedia {
private String platformName;
private List<Post> posts;
}

3. 外层建模: 最后,定义 User 类,表示社交媒体上的用户及其账户信息。

class User {
private String username;
private Map<String, SocialMedia> socialMediaAccounts;
}

重构的效果

  • 提高可读性:通过将复杂的嵌套结构转换为清晰定义的类,代码变得更加易于理解。
  • 上下文清晰:每个类都具体反映了其在应用中的角色和功能,为数据提供了清晰的上下文。
  • 减少维护难度:这种结构使得添加新功能或调整现有功能更加方便,减少了错误的可能性。