choosenimを読む - Day2 -


Day2 src/choosenimpkg/telemetory.nim

Day2はtelemetory.nimを読んでいく。
私は賢くないので何度もコードを読み返していくが、賢い人は流し読みで理解したら読まなくていいと思う。

初手

とりあえずアウトラインを眺めてみる。

「ん?report()が2つあるぞ?」と思って先にコードをチラ見したら宣言だった。
先に宣言することで、loadAnalyticsよりも後に記述されているreportを呼び出す事ができる。

個人的には宣言よりも先行定義(呼び出されるよりも先に関数を定義する)のほうがメンテナンスは楽だと思う。
呼び出される関数は前(上)にあることが保証されているし、宣言と定義で2回書かなくてもいい。
ただ宣言にもメリットはあって、ありきたりな名前のreport関数がこのモジュール内に存在することが明示的になり「これはオリジナルな関数なんだな」ということがわかって、可読性がよくなる。
まぁ用法用量を守ればだと思いますが…。

流し読み

さて流し読みをしていく。
どうやって読んだコードを解説したらいいか試行錯誤中なので、唐突に進め方が変わるかもしれない。ご容赦をば。
今日は先頭から処理ごとに読んで感想や気になったところを述べていくことにする。
気にならないような処理とかはスキップするかも。

import

  • [疑問] よく知らないanalyticsパッケージが出てきた。
  • [感想] nimblepkg/cliはエラーやINFOの標準出力のために使っている。display()でエラーの見た目が良くなるから使っているみたいだ。
  • [感想] when defined(os)は見慣れてきた。

型定義

  • [感想] enum使ったりOption使ったりしてる。モダンだなぁ(小学生並みの感想)

proc initEvent

  • [感想] 他の関数もそうだけど引数が長くなると改行入れて読みやすくしてるなぁ。
  • [感想] 47行目は汎用性を高めるために、action引数が省略されればcmd、あればそれを返すようにしてる。
    • [補足] 調べてみたところ明示的にaction引数を渡しているのは以下。
      • builder.nim 92, 132, 146行目のbuilder.build.params: CliParams
      • choosenim.nim 238行目のchoosenim.performAction.params: CliParams 1
      • choosenim.nim 273行目のchoosenim.params: CliParams1

proc initTiming

  • [感想] コメントに仕様が書いてある通り。

proc promptCustom

  • [感想]対話プロンプトを独自実装せずにnimblepkgと共通した処理で見た目とか良くしている印象。
  • [疑問]正直なところelseの場合の再帰処理の意図がよくわからない。
    • [補足] 再帰ではありません。オーバーロードでnimblepkg/cli.promptが呼び出されます。勝手にハマって、すごく脱線してたので余談に書きました→ForcePrompt

proc analyticsPrompt

  • [感想] デフォルトだと~/.choosenim/analyticsにClientIDが書き出される。

エラーハンドリングはchoosenim.nimのisMainModuleブロックで行われています。
トレースするとchoosenim(isMainModule)loadAnalyticsanalyticsPromptでした。

ちなみにClientIDの払い出しに使われている処理を追いかけてみたらこんな感じでした。
https://github.com/dom96/analytics/blob/master/src/analytics.nim#L129

https://github.com/pragmagic/uuids/blob/master/uuids.nim#L76

loadAnalytics

  • [感想] インデント深くしないようにelse省略良くやる。個人的に読みやすい。
  • [感想]analyticsFileの有無でanalyticsPromptを呼び出してる。
    • [補足]テレメトリーに同意しない場合はclientIDが空なので、データは送られない。

proc reportAsyncError

  • [感想] 非同期処理のコールバックは初めて見た。

proc hasPendingReports

  • [感想] pendingReportsがあるかどうかを返しているだけ。

waitForReport

  • operationsとrepotsが終わるか、指定秒数を超えるまでのWait処理。

proc report

  • [発見] 引数のobj: Event | Timing | ref Exceptionはobjの型がEventorTimingorref Exceptionという書き方。ジェネリクスっぽい。
    • [感想] 共通の処理があって一部だけ個別にしたいケースで、オーバーロードより便利そう。
    • [疑問] ジェネリクスではないのは何故だろう。。。予測できない入力を受け付けないため?
  • [感想] コンパイル時にこの関数に渡される型はわかるためwhen obj in Evvent:で分岐している。

今日のまとめ

nimblepkg/cliのdisplay()で出力の見栄えをよくしている。これは活用できる。
Google Analyticsを活用して、テレメトリーデータの収集をしていたので今後、収集するようなアプリの参考になる。
それにしてもきれいに設計されてるなーと思う。report()とか特に抽象化がうまいなぁ。

残っているタスク

  • [タスク]telemetry.loadAnalyticsを読む
  • [タスク]telemetry.waitForReportを読む。
  • [タスク]cliparams.parseCliParamsを読む
  • [タスク]choosenimpkgをすべて読んだらupdate,show,versions,chooseを読み直す

明日やること

  • cliparams.nimを読む

余談

ForcePrompt

proc promptCustomで気になった処理。

telemetry.nim
proc promptCustom(msg: string, params: CliParams): string =
  if params.nimbleOptions.forcePrompts == forcePromptYes:
    display("Prompt:", msg, Warning, HighPriority)
    display("Answer:", "Forced Yes", Warning, HighPriority)
    return "y"
  else:
    return promptCustom(msg, "")

このelseの再帰の意図がよくわからないので、処理を考える。
まず、forcePrompts == forcePromptYesfalseになるケースを考える。

nimble/cli.nim
   ForcePrompt* = enum
     dontForcePrompt, forcePromptYes, forcePromptNo

引用元:https://github.com/nim-lang/nimble/blob/master/src/nimblepkg/cli.nim

forcePromptsがdontForcePromptorforcePromptNoの場合にfalseになる。
でもなんで再帰…ん…?

「あっ、これ別のメソッドだ。オーバーロードじゃん!!」

そもそも引数の型がpromptCustom(msg:string, "":string)の時点で気づきたかった。
最近、再帰処理をよく見ていたので脳みそが勝手に勘違いしていた。

66行目でよばれるcli.promptCustom()は以下です。
https://github.com/nim-lang/nimble/blob/master/src/nimblepkg/cli.nim#L181

めでたしめでたし。


  1. performActions.paramsはchoosenim.paramsが渡されるので両者は同等。