[インデックス 10967] ファイルの概要
このコミットは、Goプロジェクトのダッシュボード(ビルド結果表示ページ)のユーザーインターフェースを改善し、特に横方向の表示効率を高めることを目的としています。これにより、より多くのビルド結果を狭い画面スペースでも表示できるようになり、NetBSDやPlan 9といった新しいプラットフォームのビルド結果を表示する余地が生まれています。具体的には、ビルダをOSごとにカラムでグループ化し、冗長な情報をツールチップに移動することで、視覚的な「圧縮」(horizontal crunch)を実現しています。
コミット
commit 0b702937f1fae92818531c601e366bf6a767672b
Author: Russ Cox <rsc@golang.org>
Date: Thu Dec 22 10:22:38 2011 -0500
dashboard: horizontal crunch
* group builders in to columns by OS
* drop builder suffix (moved to hover title)
* cut all domain names from email (full name+email in hover title)
* make ok smaller
This should easily give us room for netbsd and plan9,
even on small laptop screens.
Running at http://build-rsc.golang.org/.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5501064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b702937f1fae92818531c601e366bf6a767672b
元コミット内容
dashboard: horizontal crunch
* group builders in to columns by OS
* drop builder suffix (moved to hover title)
* cut all domain names from email (full name+email in hover title)
* make ok smaller
This should easily give us room for netbsd and plan9,
even on small laptop screens.
Running at http://build-rsc.golang.org/.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5501064
変更の背景
このコミットの主な背景は、Goプロジェクトのビルドダッシュボードの表示領域の最適化です。当時のダッシュボードは、ビルド結果を横方向に表示する際にスペースを多く消費しており、特に小型のラップトップ画面では表示できる情報量が限られていました。
コミットメッセージに明記されているように、以下の課題を解決するために変更が行われました。
- 表示スペースの不足: ビルド結果の表示が横に広がりすぎ、多くのビルダを一覧表示する際にスクロールが必要になる、または一部のビルダが表示しきれない問題がありました。
- 新しいプラットフォームのサポート: NetBSDやPlan 9といった新しいオペレーティングシステム向けのビルダを追加する計画がありましたが、既存のUIではそれらを受け入れる十分なスペースがありませんでした。
- 情報の冗長性: ビルダ名やユーザーのメールアドレスに冗長な情報が含まれており、これが表示スペースを圧迫していました。
- 視認性の改善: 「OK」ステータスの表示が大きすぎると判断され、よりコンパクトにする必要がありました。
これらの課題を解決し、ダッシュボードのユーザビリティとスケーラビリティを向上させるために、「horizontal crunch」(横方向の圧縮)というアプローチが取られました。
前提知識の解説
このコミットを理解するためには、以下の技術的背景知識が役立ちます。
-
Go言語の
html/template
パッケージ:- Go言語の標準ライブラリに含まれるテンプレートエンジンです。HTMLコンテンツを動的に生成するために使用されます。
template.FuncMap
は、テンプレート内で呼び出すことができるカスタム関数を登録するためのマップです。これにより、テンプレートロジックをGoのコードで記述し、テンプレートから利用できるようになります。{{.FieldName}}
のような構文で、テンプレートに渡されたデータ構造のフィールドにアクセスします。{{range .Slice}}...{{end}}
のような構文で、スライスやマップをイテレートします。{{if .Condition}}...{{end}}
のような構文で条件分岐を行います。
-
Go言語の
strings
パッケージ:- 文字列操作のためのユーティリティ関数を提供します。
strings.Index(s, substr)
: 文字列s
内でsubstr
が最初に出現するインデックスを返します。見つからない場合は-1を返します。strings.Split(s, sep)
: 文字列s
を区切り文字sep
で分割し、文字列のスライスを返します。
-
HTMLのテーブル構造とCSS:
<table>
、<thead>
、<tbody>
、<tr>
、<th>
、<td>
: HTMLで表を作成するための基本的な要素です。colspan
属性:<th>
または<td>
要素に適用され、そのセルが横方向にいくつの列を結合するかを指定します。このコミットでは、OSごとのビルダグループの見出しを作成するために使用されています。colgroup
および<col>
要素: テーブルの列のグループを定義し、そのグループ内の列にスタイルを適用するために使用されます。このコミットでは、特定の列グループに境界線などのスタイルを適用するために利用されています。- CSSの
width
、font-size
、border-right
:width
: 要素の幅を設定します。font-size
: テキストのフォントサイズを設定します。border-right
: 要素の右側に境界線を追加します。
title
属性: ほとんどのHTML要素に適用でき、要素の上にマウスカーソルを置いたときに表示されるツールチップテキストを提供します。このコミットでは、省略された情報を元の完全な形で表示するために活用されています。
-
Google App Engine (GAE) と
memcache
:memcache
は、Google App Engineなどのクラウド環境で利用される分散型メモリキャッシュサービスです。頻繁にアクセスされるデータを一時的にメモリに保存することで、データベースへのアクセスを減らし、アプリケーションのパフォーマンスを向上させます。- このコミットの
ui.go
に見られるmemcache.Get
やmemcache.Set
の利用は、ダッシュボードのフロントページをキャッシュして、リクエスト処理の高速化を図っていることを示唆しています。
これらの知識は、コミットがGo言語のWebアプリケーションのフロントエンドとバックエンドの両方にわたる変更であり、特に表示効率とユーザーエクスペリエンスの改善に焦点を当てていることを理解する上で不可欠です。
技術的詳細
このコミットは、Goダッシュボードの表示ロジックとHTMLテンプレートの両方にわたる変更を含んでいます。主な技術的変更点は以下の通りです。
misc/dashboard/app/build/ui.go
の変更
-
キャッシュロジックの変更:
uiHandler
関数内で、フロントページのキャッシュを読み書きする条件がpage == 0
からpage == 0 && r.Host == "build.golang.org"
に変更されました。- これは、キャッシュが本番環境(
build.golang.org
)でのみ有効になるように制限することで、開発環境やテスト環境でのキャッシュによる予期せぬ動作を防ぎ、デバッグを容易にするための変更と考えられます。
-
新しいテンプレート関数の追加:
tmplFuncs
に以下の新しいカスタムテンプレート関数が追加されました。これらは、ビルダの情報をより細かく抽出し、表示を制御するために使用されます。builderOS(s string) string
: ビルダ文字列(例: "linux-amd64-foo")からOS部分(例: "linux")を抽出します。builderArch(s string) string
: ビルダ文字列からアーキテクチャ部分(例: "amd64")を抽出します。builderArchShort(s string) string
: アーキテクチャ名を短縮形(例: "amd64" -> "x64")に変換します。builderArchChar(s string) string
: アーキテクチャ名を1文字のコード(例: "386" -> "8", "amd64" -> "6")に変換します。これは、おそらくアイコンや非常にコンパクトな表示のために使用されます。builderSpans(s []string) []builderSpan
: ビルダのリストを受け取り、OSごとにグループ化されたスパン情報(OS名と、そのOSに属するビルダの数)を返します。これはHTMLテーブルのcolspan
属性を動的に生成するために不可欠です。
-
shortUser
関数の改善:- ユーザーのメールアドレスからドメイン名を削除するロジックが変更されました。以前は
@golang.org
のみを削除していましたが、新しいロジックでは@
以降のすべてのドメイン名を削除し、ユーザー名のみを抽出します。 - これにより、表示されるユーザー名がより短くなり、横方向のスペースを節約できます。完全なメールアドレスは、HTMLの
title
属性(ツールチップ)として提供されるようになります。
- ユーザーのメールアドレスからドメイン名を削除するロジックが変更されました。以前は
misc/dashboard/app/build/ui.html
の変更
-
CSSスタイルの調整:
.build .result
のwidth
が50px
から2em
に縮小されました。em
単位を使用することで、フォントサイズに基づいて相対的に幅が調整されるようになります。.col-hash
,.col-result
にborder-right: solid 1px #ccc;
が追加され、列間に視覚的な区切りが追加されました。.build .arch
クラスが追加され、font-size: 66%;
とfont-weight: normal;
が適用され、アーキテクチャ表示が小さく、目立たないように変更されました。.build .ok
クラスが追加され、font-size: 83%;
が適用され、「OK」ステータスの表示が小さくなりました。
-
HTMLテーブル構造の再構築:
colgroup
の導入:colgroup class="col-hash"
: コミットハッシュ列のグループ。{{range $.Builders | builderSpans}} <colgroup class="col-result" span="{{.N}}"></colgroup> {{end}}
:builderSpans
関数によって生成されたOSごとのグループに対応するcolgroup
が動的に生成されます。span="{{.N}}"
により、そのOSに属するビルダの数だけ列が結合されます。colgroup class="col-user"
,colgroup class="col-time"
,colgroup class="col-desc"
: ユーザー、時間、説明の各列のグループ。
- 新しいヘッダー行の追加:
<th> </th>
の後に、{{range $.Builders | builderSpans}} <th colspan="{{.N}}">{{.OS}}</th> {{end}}
という新しいヘッダー行が追加されました。これにより、OS名がそのOSに属するビルダの列全体にわたって表示され、ビルダがOSごとにグループ化されていることが視覚的に明確になります。
- ビルダ名の表示変更:
- 以前は
{{builderTitle .}}
でビルダの完全なタイトルを表示していた<th>
が、{{builderArchShort .}}
で短縮されたアーキテクチャ名を表示するように変更されました。 - 元の完全なビルダ名は
title="{{.}}"
属性として追加され、マウスオーバーで表示されるようになりました。
- 以前は
- ユーザー名と説明の表示変更:
shortUser .User
とshortDesc .Desc
の表示はそのままですが、それぞれにtitle
属性が追加され、元の完全なユーザー名と説明がツールチップとして表示されるようになりました。
これらの変更により、ダッシュボードはよりコンパクトになり、視覚的に整理され、限られた画面スペースでもより多くの情報を提供できるようになりました。
コアとなるコードの変更箇所
misc/dashboard/app/build/ui.go
@@ -37,7 +37,7 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
}
// Used cached version of front page, if available.
- if page == 0 {
+ if page == 0 && r.Host == "build.golang.org" {
t, err := memcache.Get(c, uiCacheKey)
if err == nil {
w.Write(t.Value)
@@ -78,7 +78,7 @@ func uiHandler(w http.ResponseWriter, r *http.Request) {
}
// Cache the front page.
- if page == 0 {
+ if page == 0 && r.Host == "build.golang.org" {
t := &memcache.Item{
Key: uiCacheKey,
Value: buf.Bytes(),
@@ -179,12 +179,84 @@ var uiTemplate = template.Must(
)
var tmplFuncs = template.FuncMap{
-\t"builderTitle": builderTitle,\n-\t"repoURL": repoURL,\n-\t"shortDesc": shortDesc,\n-\t"shortHash": shortHash,\n-\t"shortUser": shortUser,\n-\t"tail": tail,\n+\t"builderOS": builderOS,\n+\t"builderArch": builderArch,\n+\t"builderArchShort": builderArchShort,\n+\t"builderArchChar": builderArchChar,\n+\t"builderTitle": builderTitle,\n+\t"builderSpans": builderSpans,\n+\t"repoURL": repoURL,\n+\t"shortDesc": shortDesc,\n+\t"shortHash": shortHash,\n+\t"shortUser": shortUser,\n+\t"tail": tail,\n+}\n+\n+func splitDash(s string) (string, string) {\n+\ti := strings.Index(s, \"-\")\n+\tif i >= 0 {\n+\t\treturn s[:i], s[i+1:]\n+\t}\n+\treturn s, \"\"\n+}\n+\n+// builderOS returns the os tag for a builder string\n+func builderOS(s string) string {\n+\tos, _ := splitDash(s)\n+\treturn os\n+}\n+\n+// builderArch returns the arch tag for a builder string\n+func builderArch(s string) string {\n+\t_, arch := splitDash(s)\n+\tarch, _ = splitDash(arch) // chop third part\n+\treturn arch\n+}\n+\n+// builderArchShort returns a short arch tag for a builder string\n+func builderArchShort(s string) string {\n+\tarch := builderArch(s)\n+\tswitch arch {\n+\tcase \"amd64\":\n+\t\treturn \"x64\"\n+\t}\n+\treturn arch\n+}\n+\n+// builderArchChar returns the architecture letter for a builder string\n+func builderArchChar(s string) string {\n+\tarch := builderArch(s)\n+\tswitch arch {\n+\tcase \"386\":\n+\t\treturn \"8\"\n+\tcase \"amd64\":\n+\t\treturn \"6\"\n+\tcase \"arm\":\n+\t\treturn \"5\"\n+\t}\n+\treturn arch\n+}\n+\n+type builderSpan struct {\n+\tN int\n+\tOS string\n+}\n+\n+// builderSpans creates a list of tags showing\n+// the builder\'s operating system names, spanning\n+// the appropriate number of columns.\n+func builderSpans(s []string) []builderSpan {\n+\tvar sp []builderSpan\n+\tfor len(s) > 0 {\n+\t\ti := 1\n+\t\tos := builderOS(s[0])\n+\t\tfor i < len(s) && builderOS(s[i]) == os {\n+\t\t\ti++\n+\t\t}\n+\t\tsp = append(sp, builderSpan{i, os})\n+\t\ts = s[i:]\n+\t}\n+\treturn sp\n }\n \n // builderTitle formats \"linux-amd64-foo\" as \"linux amd64 foo\".\n@@ -210,11 +282,11 @@ func shortHash(hash string) string {\n \n // shortUser returns a shortened version of a user string.\n func shortUser(user string) string {\n-\tif i, j := strings.Index(user, \"<\"), strings.Index(user, \">\"); i != -1 && j > i {\n+\tif i, j := strings.Index(user, \"<\"), strings.Index(user, \">\"); 0 <= i && i < j {\n \t\tuser = user[i+1 : j]\n-\t\tif k := strings.Index(user, \"@golang.org\"); k != -1 {\n-\t\t\tuser = user[:k]\n-\t\t}\n+\t}\n+\tif i := strings.Index(user, \"@\"); i >= 0 {\n+\t\treturn user[:i]\n \t}\n \treturn user\n }\n```
### `misc/dashboard/app/build/ui.html`
```html
@@ -35,11 +35,21 @@
}\n .build .result {\n text-align: center;\n- width: 50px;\n+ width: 2em;\n+ }\n+ .col-hash, .col-result {\n+ border-right: solid 1px #ccc;\n+ }\n+ .build .arch {\n+ font-size: 66%;\n+ font-weight: normal;\n }\n .build .time {\n color: #666;\n }\n+ .build .ok {\n+ font-size: 83%;\n+ }\n .build .desc, .build .time, .build .user {\n white-space: nowrap;\n }\n@@ -66,10 +76,29 @@\n {{if $.Commits}}\n \n <table class=\"build\">\n+ <colgroup class=\"col-hash\"></colgroup>\n+ {{range $.Builders | builderSpans}}\n+ <colgroup class=\"col-result\" span=\"{{.N}}\"></colgroup>\n+ {{end}}\n+ <colgroup class=\"col-user\"></colgroup>\n+ <colgroup class=\"col-time\"></colgroup>\n+ <colgroup class=\"col-desc\"></colgroup>\n+ <tr>\n+ <!-- extra row to make alternating colors use dark for first result -->\n+ </tr>\n+ <tr>\n+ <th> </th>\n+ {{range $.Builders | builderSpans}}\n+ <th colspan=\"{{.N}}\">{{.OS}}</th>\n+ {{end}}\n+ <th></th>\n+ <th></th>\n+ <th></th>\n+ </tr>\n <tr>\n <th> </th>\n {{range $.Builders}}\n- <th class=\"result\">{{builderTitle .}}</th>\n+ <th class=\"result arch\" title=\"{{.}}\">{{builderArchShort .}}</th>\n {{end}}\n </tr>\n {{range $c := $.Commits}}\n@@ -88,9 +117,9 @@\n {{end}}\n </td>\n {{end}}\n- <td class=\"user\">{{shortUser .User}}</td>\n+ <td class=\"user\" title=\"{{.User}}\">{{shortUser .User}}</td>\n <td class=\"time\">{{.Time.Time.Format \"Mon 02 Jan 15:04\"}}</td>\n- <td class=\"desc\">{{shortDesc .Desc}}</td>\n+ <td class=\"desc\" title=\"{{.Desc}}\">{{shortDesc .Desc}}</td>\n </tr>\n {{end}}\n </table>\n```
## コアとなるコードの解説
### `misc/dashboard/app/build/ui.go` の解説
1. **キャッシュ条件の厳格化**:
* `if page == 0 && r.Host == "build.golang.org"`: `uiHandler`関数内の`memcache`の利用条件に`r.Host == "build.golang.org"`が追加されました。これは、ダッシュボードのフロントページ(`page == 0`)のキャッシュが、本番環境のホスト名(`build.golang.org`)でのみ有効になるようにすることで、開発やテスト環境でのキャッシュによる不整合やデバッグの困難さを避けるための変更です。
2. **ビルダ情報抽出・整形関数の追加**:
* `splitDash(s string) (string, string)`: ビルダ文字列を最初のハイフンで分割するヘルパー関数です。
* `builderOS(s string) string`: ビルダ文字列からOS名(例: "linux-amd64" から "linux")を抽出します。
* `builderArch(s string) string`: ビルダ文字列からアーキテクチャ名(例: "linux-amd64" から "amd64")を抽出します。
* `builderArchShort(s string) string`: アーキテクチャ名を短縮形(例: "amd64" を "x64")に変換します。これは、UI上での表示スペースを節約するために使用されます。
* `builderArchChar(s string) string`: アーキテクチャ名を特定の1文字(例: "386" を "8", "amd64" を "6")に変換します。これは、さらにコンパクトな表示や、特定のアイコン表示に利用される可能性があります。
* `builderSpans(s []string) []builderSpan`: 最も重要な追加関数の一つです。ビルダのリストを受け取り、同じOSを持つビルダをグループ化し、各OSグループが占めるべき列数(`N`)とOS名(`OS`)を含む`builderSpan`構造体のスライスを返します。この情報は、HTMLテンプレートで`colspan`属性を動的に設定するために使用され、OSごとのヘッダーを生成する基盤となります。
3. **`shortUser`関数の汎用化**:
* `if i := strings.Index(user, "@"); i >= 0 { return user[:i] }`: ユーザーのメールアドレスからドメイン名を削除するロジックが改善されました。以前は`@golang.org`という特定のドメインのみを対象としていましたが、この変更により、`@`記号以降のすべての部分が削除され、より汎用的にユーザー名のみを抽出するようになりました。これにより、表示されるユーザー名が短縮され、横方向のスペースが節約されます。完全なメールアドレスは、HTMLの`title`属性としてツールチップで表示されるようになります。
### `misc/dashboard/app/build/ui.html` の解説
1. **CSSによる表示の最適化**:
* `.build .result`の`width`を`50px`から`2em`に変更することで、ビルド結果のセル幅を相対的に小さくし、より多くのビルダを横に並べられるようにしました。
* `.col-hash, .col-result`に`border-right`を追加することで、コミットハッシュ列とビルド結果列の間に視覚的な区切りを設け、可読性を向上させました。
* `.build .arch`と`.build .ok`の`font-size`を調整することで、アーキテクチャ名と「OK」ステータスの表示を小さくし、全体的なコンパクトさを実現しました。
2. **HTMLテーブル構造の動的な再構築**:
* **`colgroup`の導入**: `colgroup`要素と`span`属性を組み合わせることで、テーブルの列のグループを定義し、CSSでスタイルを適用できるようにしました。特に、`{{range $.Builders | builderSpans}} <colgroup class="col-result" span="{{.N}}"></colgroup> {{end}}` の部分は、Goコードで計算された`builderSpans`情報に基づいて、OSごとのビルダグループに対応する列グループを動的に生成し、それぞれのグループが適切な数の列を占めるようにしています。
* **OSごとのヘッダー行の追加**: 新しい`<tr>`要素が追加され、その中に`{{range $.Builders | builderSpans}} <th colspan="{{.N}}">{{.OS}}</th> {{end}}` が含まれています。これにより、各OS名がそのOSに属するすべてのビルダ列にわたって表示される大きな見出しとなり、ビルダがOSごとに明確にグループ化されていることを示します。
* **ビルダ名の表示変更とツールチップの活用**: 以前は`{{builderTitle .}}`で完全なビルダ名を表示していた`<th>`が、`{{builderArchShort .}}`で短縮されたアーキテクチャ名を表示するように変更されました。同時に、`title="{{.}}" `属性が追加され、マウスカーソルを合わせると元の完全なビルダ名が表示されるようになりました。これにより、デフォルトの表示はコンパクトに保ちつつ、必要に応じて詳細情報にアクセスできるようになります。
* **ユーザー名と説明へのツールチップ追加**: `shortUser .User`と`shortDesc .Desc`の表示はそのままですが、それぞれに`title`属性が追加され、マウスオーバーで完全なユーザー名と説明が表示されるようになりました。これも同様に、表示スペースの節約と情報アクセシビリティの両立を図るための変更です。
これらの変更は、Goダッシュボードの視覚的な密度を高め、より多くの情報を一度に表示できるようにすることで、ユーザーエクスペリエンスを大幅に向上させています。特に、OSごとのビルダのグループ化と、詳細情報をツールチップに移動するアプローチは、限られた画面スペースでの情報表示において非常に効果的なデザインパターンです。
## 関連リンク
* Go CL (Change List): [https://golang.org/cl/5501064](https://golang.org/cl/5501064)
## 参考にした情報源リンク
* Go言語 `html/template` パッケージ: [https://pkg.go.dev/html/template](https://pkg.go.dev/html/template)
* Go言語 `strings` パッケージ: [https://pkg.go.dev/strings](https://pkg.go.dev/strings)
* HTML `<table>` 要素: [https://developer.mozilla.org/ja/docs/Web/HTML/Element/table](https://developer.mozilla.org/ja/docs/Web/HTML/Element/table)
* HTML `colspan` 属性: [https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/colspan](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/colspan)
* HTML `colgroup` 要素: [https://developer.mozilla.org/ja/docs/Web/HTML/Element/colgroup](https://developer.mozilla.org/ja/docs/Web/HTML/Element/colgroup)
* HTML `title` 属性: [https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/title](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/title)
* CSS `width` プロパティ: [https://developer.mozilla.org/ja/docs/Web/CSS/width](https://developer.mozilla.org/ja/docs/Web/CSS/width)
* CSS `font-size` プロパティ: [https://developer.mozilla.org/ja/docs/Web/CSS/font-size](https://developer.mozilla.org/ja/docs/Web/CSS/font-size)
* CSS `border-right` プロパティ: [https://developer.mozilla.org/ja/docs/Web/CSS/border-right](https://developer.mozilla.org/ja/docs/Web/CSS/border-right)
* Google App Engine Memcache: (当時のドキュメントは変更されている可能性がありますが、概念的な理解のために) [https://cloud.google.com/appengine/docs/standard/go/memcache/](https://cloud.google.com/appengine/docs/standard/go/memcache/) (現在のGo向けGAEドキュメント)