Newer posts are loading.
You are at the newest post.
Click here to check if anything new just came in.

November 17 2017

JSON-LD に対応してみた - remark

以前,このサイトを Twitter Cards に対応させたのだが,今回も思いつきで JSON-LD に対応させることにした。

真面目に JSON-LD を扱うとなると相当凝ったことができるみたいだが1,今回は軽めに Google の検索サービスが解釈できる範囲で調整してみようと思う。

なお JSON-LD を導入するのなら Microdata の記述はページから削除することをお勧めする。 Microdata は既に開発が終了しており,もはや推奨されない。

Web ページに JSON-LD を埋め込む

まず JSON-LD を Web ページに埋め込む際は以下のように <script> 要素で囲む。

<script type="application/ld+json">
{
  ...
}
</script>

メディア・タイプに注意すること。 どうやらこの記述はページ内にいくつ置いてもいいようだ(少なくとも Google のテストツールには怒られなかった)。

<script type="application/ld+json">
{
  ...
}
</script>

<script type="application/ld+json">
{
  ...
}
</script>

JSON-LD の詳細

JSON-LD の中身だが,まずは @context@type を指定する。

{
  "@context": "http://schema.org",
  "@type": "WebSite"
}

@context で語彙を定義するのだが,どうやら Google は Microdata の語彙(のひとつ)である schema.org を前提にしているらしい。 @type には schema.org で定義される content type を指定する。 このサイトは一応ブログサイトなので,以下の content type を使うことにする。

まずは WebSite から。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "WebSite",
  "@ID": "http://text.baldanders.info/",
  "inLanguage": "ja",
  "name": "text.Baldanders.info",
  "url": "http://text.baldanders.info/",
  "publisher": {
  "@ID": "http://text.baldanders.info/#org"
  },
  "author": {
    "@type": "Person",
    "@ID": "http://text.baldanders.info/#maker",
    "name": "Spiegel",
    "url": "http://www.baldanders.info/spiegel/profile/",
    "image": "http://text.baldanders.info/images/avatar.jpg"
  },
  "image": "http://text.baldanders.info/images/avatar.jpg",
  "description": "帰ってきた「しっぽのさきっちょ」"
}
</script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Organization",
  "@ID": "http://text.baldanders.info/#org",
  "name": "text.Baldanders.info",
  "logo": {
    "@type": "ImageObject",
    "@ID": "http://text.baldanders.info/#logo",
    "url": "http://text.baldanders.info/images/avatar.jpg"
  }
}
</script>

@ID を使って publisher の内容を参照しているのがお分かりだろうか。 これをテストツールにかけるとこんな感じになる。

Structured Data Testing Tool (1) Structured Data Testing Tool (1)

publisherOrganization の情報が入っているのがわかると思う。

本来 publisher には OrganizationPerson のどちらも有効な筈なのだが,テストツールOrganization しか受け付けないようだ。 これだとうちのような個人サイトは大変困るのだが,しょうがないので Organization にテキトーな情報を入れてお茶を濁している。 何とかしてよ Google 先生!

次は Blog の情報をセットする。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Blog",
  "@ID": "http://text.baldanders.info/remark/",
  "url": "http://text.baldanders.info/remark/",
  "inLanguage": "ja",
  "name": "しっぽのさきっちょ",
  "description": "とりとめのない四方山話。",
  "image": "http://text.baldanders.info/images/attention/remark.jpg",
  "publisher": {
    "@ID": "http://text.baldanders.info/#org"
  },
  "author": {
    "@type": "Person",
    "@ID": "http://text.baldanders.info/remark/#maker",
    "name": "Spiegel",
    "url": "http://www.baldanders.info/spiegel/profile/",
    "image": "http://text.baldanders.info/images/avatar.jpg"
  }
}
</script>

これをテストツールにかけるとこんな感じになる。

Structured Data Testing Tool (2) Structured Data Testing Tool (2)

どんどん行こう。 BlogPosting はこんな感じ。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BlogPosting",
  "@ID": "http://text.baldanders.info/remark/2017/11/qiitadon/",
  "url": "http://text.baldanders.info/remark/2017/11/qiitadon/",
  "mainEntityOfPage": "http://text.baldanders.info/remark/2017/11/qiitadon/",
  "inLanguage": "ja",
  "name": "Qiita って Mastodon やってたのか",
  "description": "私にとって今年最初の大外しは「GW 過ぎたら Mastodon のことなんかみんな忘れてる」だったが,本当に忘れていたのは私だけだったようだ。",
  "headline": "私にとって今年最初の大外しは「GW 過ぎたら Mastodon のことなんかみんな忘れてる」だったが,本当に忘れていたのは私だけだったようだ。",
  "keywords": "mastodon, communication",
  "image": "http://text.baldanders.info/images/attention/remark.jpg",
  "datePublished": "2017-11-10T13:49:58+09:00",
  "dateModified": "2017-11-16T10:09:40+09:00",
  "publisher": {
    "@ID": "http://text.baldanders.info/#org"
  },
  "author": {
    "@type": "Person",
    "@ID": "http://text.baldanders.info/remark/2017/11/qiitadon/#maker",
    "name": "Spiegel",
    "url": "http://www.baldanders.info/spiegel/profile/",
    "image": "http://text.baldanders.info/images/avatar.jpg"
  },
  "license": "http://creativecommons.org/licenses/by-sa/4.0/"
}
</script>

Google は BlogPosting の内容を利用している。 Google が BlogPosting で要求するプロパティは以下の通り。

Properties Data Type AMP non-AMP mainEntityOfPage URL recommended ignored headline Text required recommended image ImageObject or URL required recommended publisher Organization required ignored publisher.name Text required ignored publisher.logo ImageObject required ignored publisher.logo.url URL required ignored publisher.logo.height Number required ignored publisher.logo.width Number required ignored datePublished DateTime required ignored dateModified DateTime required ignored author Person or Organization required ignored author.name Text required ignored description Text recommended ignored

AMP (Accelerated Mobile Pages) と non-AMP で要求が異なるが,テストツールは AMP を前提にしているようで,これらのプロパティがないとエラーまたは警告を吐く2

これもテストツールにかけてみよう。 こんな感じ。

Structured Data Testing Tool (3) Structured Data Testing Tool (3)

最後に BreadcrumbList (パンくずリスト3)はこんな感じになる。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "@ID": "http://text.baldanders.info/remark/2017/11/qiitadon/#breadcrumb-list",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position": 1,
      "item": {
        "@ID": "http://text.baldanders.info/"
      }
    },
    {
      "@type": "ListItem",
      "position": 2,
      "item": {
        "@ID": "http://text.baldanders.info/remark/"
      }
    }
  ]
}
</script>

Google は BreadcrumbList の内容も利用している。 Google が BreadcrumbList で要求するプロパティは以下の通り。

Properties Data Type Requirement image URL optional item Thing required name Text required position Integer required

上述のコードでは item@ID の参照先と繋げている。 具体的には,最初の階層に WebSite@ID を,2番目の階層に Blog@ID を指定している。 これによって item の中に name が含まれるため,不足なく情報をセットできている。

ブックマーク


  1. JSON-LD の仕様を見るかぎり,Microdata や RDFa の語彙を流用したり自前で語彙を作ったりできるっぽい。 [return]
  2. ただし publisher.logo.heightpublisher.logo.width は例外のようで,これらのプロパティがなくてもテストツールではエラーにならなかった。まぁ画像のサイズは画像データから読み取れるからね。 [return]
  3. 一応説明しておくと「パンくずリスト」とはサイト内のページ位置を見失わないようにするためのナビゲーション表示を指す。名前で想像される通り「ヘンゼルとグレーテル」の童話が語源になっているらしい。まぁ,いまどきパンくずリストがないと迷子になってしまうようなダサい構成のサイトは少なくなってると思うけど。 [return]
Structured Data Testing Tool (3)
Structured Data Testing Tool (2)
Structured Data Testing Tool (1)

November 10 2017

Qiita って Mastodon やってたのか - remark

私にとって2017年最初の大外しは「GW 過ぎたら Mastodon のことなんかみんな忘れてる」だったが,本当に忘れていたのは私だけだったようだ。

Qiita の記事を眺めててふと見ると「Toot」ボタンが追加されているのに気がついた。

Qiitadon Button Qiitadon Button

Pixiv が Mastodon を運営してるってのは知ってたし,こういうやり方は上手いと感じていたので「Qiita もやればいいのに」と当時は思っていたが,2017年5月末には既に始めてたのか。 いや,もう,完全にスルーしてたよ。

というわけで,私も spiegel">@spiegel@qiitadon.com でサインアップした。 ついでに Subway Tooter も入れてみた。 まぁ,正直に言って Time-Line サービスには飽き飽きしている部分もあって,あんまり積極的には使わないかもしれないが,少なくとも Qiita の記事に「Tweet」ボタンを押すことはなくなるだろう。 このサイトに「Toot」ボタンを付けるかどうかはこれから考える。

Mastodon は分散システムであることが強みだとよく言われるが,言い方を変えると「Mastodon だけ」のサービスには殆ど存在意義がない。 PawooQiitadon のように既に生態系を形成しているサービスに追加するなら意味はあると思うが。 それとも結城浩さんのように自前でインスタンスを立ててブランド戦略に組み込むか。

言い訳がましくてホントにゴメンナサイだけど, Mastodon の話題ってちゃんと意識してないと本当に何も聞こえてこない。 ホンマに流行ってるのか? 英語圏の記事でも Mastodon の話題なんかまず聞こえてこないし。 うーん。

ブックマーク

Qiitadon Button

November 08 2017

Firefox Quantum Developer Editon
Independent Browser

Markdown 形式のリンクを生成するツールを作ってみた - golang

きっかけはこれ。

仕様はこんな感じ。

URLを入力すると、Markdownに最適な形式で、webページタイトルとURLを出力します。出力結果はコンソールに表示すると同時に、クリップボードにもコピーされます。
via Markdownのリンクフォーマットを生成するコマンドを自作した

あれ? これ Go 言語でも簡単に作れるんじゃないかな。 ちうわけで作ってみた。

(Windows では mklink はシンボリック・リンクを作るコマンドだと後で気づいたが,後悔先に立たずw

パッケージの使い方としてはこんな感じである。

link, err := mklink.New("https://git.io/vFR5M")
if err != nil {
    fmt.Println(err)
    return
}
fmt.Println(link.Encode(mklink.StyleMarkdown))
// Output:
// [GitHub - spiegel-im-spiegel/mklink: Make Link with Markdown Format](https://github.com/spiegel-im-spiegel/mklink)

コマンドライン・インタフェースはこんな感じ。

$ mklink -h
Usage:
  mklink [flags] [URL [URL]...]

Flags:
  -h, --help           help for mklink
  -i, --interactive    interactive mode
      --log string     output log
  -s, --style string   link style (default "markdown")
  -v, --version        output version of mklink

-i オプションを付けると対話モードになる。

$ mklink -i
Press Ctrl+C to stop
mklink> https://git.io/vFR5M
[GitHub - spiegel-im-spiegel/mklink: Make Link with Markdown Format](https://github.com/spiegel-im-spiegel/mklink)
mklink>

作成したリンクを標準出力に出力すると同時にクリップボードにもコピーする。 いやぁ,これめっさ便利だわ。

goquery

URL からページのタイトルを取得するには HTML の解析を行うスクレイピング(Web scraping)機能が必要だが,好都合なことに PuerkitoBio/goquery というパッケージが公開されている。

PuerkitoBio/goquery が優れているのは jQuery っぽい仕掛けでとても簡単に HTML の解析ができる点にある。 今回は <head> 要素からタイトルと説明(description)を抜き出すだけだが,こんな感じに記述できる。

//New returns new Link instance
func New(url string) (*Link, error) {
	link := &Link{URL: url}
	doc, err := goquery.NewDocument(url)
	if err != nil {
		return link, err
	}
	link.Location = doc.Url.String()

	doc.Find("head").Each(func(_ int, s *goquery.Selection) {
		s.Find("title").Each(func(_ int, s *goquery.Selection) {
			link.Title = s.Text()
		})
		s.Find("meta[name='description']").Each(func(_ int, s *goquery.Selection) {
			if v, ok := s.Attr("content"); ok {
				link.Description = v
			}
		})
	})

	return link, nil
}

ね, jQuery ぽいでしょ。 まさか,この期に及んで jQuery (ぽいもの)を触ることになるとは思わなかったぜ。 jQuery の本は納戸に仕舞っちゃったんだけどなぁ。

ターミナルの判定

mklink はパイプでも動作する。

$ echo https://git.io/vFR5M | mklink
[GitHub - spiegel-im-spiegel/mklink: Make Link with Markdown Format](https://github.com/spiegel-im-spiegel/mklink)

この時にうっかり -i オプションを付けて(パイプのつもりが)対話モードになっては困るので標準入力がターミナルかどうかを判定するロジックを入れている。

if isatty.IsTerminal(os.Stdin.Fd()) {
    if interactiveFlag {
        interactive.New(style, log).Run()
    }
    return nil
}

この機能は mattn/go-isatty パッケージで実現している1

クリップボードの操作

クリップボードの操作といっても今回は書き込みだけだが atotto/clipboard を使って実装している。

r := lnk.Encode(c.linkStyle)
buf := new(bytes.Buffer)
io.Copy(c.writer, io.TeeReader(r, buf))
strLink := buf.String()
clipboard.WriteAll(strLink)

Windows 環境では問題なく動作しているが,他の OS ではどうなのかよく分からない。

ブックマーク


  1. 最初は golang.org/x/crypto/ssh/terminal パッケージを使っていたのだが「たしかもう少し軽いパッケージあったよなぁ」と思ってググったら思い出した。 [return]

November 07 2017

GnuPG 2.2.2 がリリースされた - remark

GnuPG 2.2.2 がリリースされた。

今回はセキュリティ・アップデートはなし。 主な修正点は以下の通り。

  • gpg: Avoid duplicate key imports by concurrently running gpg processes. [#3446]
  • gpg: Fix creating on-disk subkey with on-card primary key. [#3280]
  • gpg: Fix validity retrieval for multiple keyrings. [Debian#878812]
  • gpg: Fix --dry-run and import option show-only for secret keys.
  • gpg: Print “sec” or “sbb” for secret keys with import option import-show. [#3431]
  • gpg: Make import less verbose. [#3397]
  • gpg: Add alias “Key-Grip” for parameter “Keygrip” and new parameter “Subkey-Grip” to unattended key generation. [#3478]
  • gpg: Improve “factory-reset” command for OpenPGP cards. [#3286]
  • gpg: Ease switching Gnuk tokens into ECC mode by using the magic keysize value 25519.
  • gpgsm: Fix --with-colon listing in crt records for fields > 12.
  • gpgsm: Do not expect X.509 keyids to be unique. [#1644]
  • agent: Fix stucked Pinentry when using –max-passphrase-days. [#3190]
  • agent: New option --s2k-count. [#3276 (workaround)]
  • dirmngr: Do not follow https-to-http redirects. [#3436]
  • dirmngr: Reduce default LDAP timeout from 100 to 15 seconds. [#3487]
  • gpgconf: Ignore non-installed components for commands --apply-profile and --apply-defaults. [#3313]
  • Add configure option --enable-werror. [#2423]

最新版をインストールすると以下のようになる。

$ gpg --version
gpg (GnuPG) 2.2.2
libgcrypt 1.8.1
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: ********
サポートしているアルゴリズム:
公開鍵: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
暗号方式: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
    CAMELLIA128, CAMELLIA192, CAMELLIA256
ハッシュ: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
圧縮: 無圧縮, ZIP, ZLIB, BZIP2

アップデートは計画的に。

参考図書

photo
暗号技術入門 第3版 秘密の国のアリス
結城 浩
SBクリエイティブ 2015-08-25
評価

自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解! 数学ガールの秘密ノート/丸い三角関数 数学ガールの秘密ノート/微分を追いかけて 情報セキュリティ白書2015: サイバーセキュリティ新時代:あらゆる変化へ柔軟な対応を 数学ガールの秘密ノート/数列の広場

SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。

reviewed by Spiegel on 2015-09-20 (powered by G-Tools)

gpgpdump 0.2.0 をリリースした - remark

OpenPGP パケットの内容を視覚化する gpgpdump の 0.2.0 をリリースした。 名前でピンとくる人もいるだろうが,山本和彦さんの pgpdump の翻案である1。 特徴は以下のとおり。

これまでの 0.1.x から全面的に書き換えた。 一応 60% くらいは元のコードは残ってるかな。 不正パケットで panic になる状況はだいぶ減ったと思う。

Go 言語のパッケージとして使う場合は以下のようなコードになる。

const openpgpStr = `
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iF4EARMIAAYFAlTDCN8ACgkQMfv9qV+7+hg2HwEA6h2iFFuCBv3VrsSf2BREQaT1
T1ZprZqwRPOjiLJg9AwA/ArTwCPz7c2vmxlv7sRlRLUI6CdsOqhuO1KfYXrq7idI
=ZOTN
-----END PGP SIGNATURE-----
`

info, err := gpgpdump.Parse(strings.NewReader(openpgpStr), options.NewOptions())
if err != nil {
	return
}
fmt.Println(info.Packets[0].Value)
// Output:
// Signature Packet (tag 2)

コマンドライン・インタフェースは 0.1.x とほぼ変わらず。

$ gpgpdump -h
Usage:
  gpgpdump [flags] [PGPfile]

Flags:
  -a, --armor     accepts ASCII input only
  -h, --help      help for gpgpdump
  -i, --int       dumps multi-precision integers
  -j, --json      output with JSON format
  -l, --literal   dumps literal packets (tag 11)
  -m, --marker    dumps marker packets (tag 10)
  -p, --private   dumps private packets (tag 60-63)
  -u, --utc       output with UTC time
  -v, --version   output version of gpgpdump

$ cat sig
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iF4EARMIAAYFAlTDCN8ACgkQMfv9qV+7+hg2HwEA6h2iFFuCBv3VrsSf2BREQaT1
T1ZprZqwRPOjiLJg9AwA/ArTwCPz7c2vmxlv7sRlRLUI6CdsOqhuO1KfYXrq7idI
=ZOTN
-----END PGP SIGNATURE-----

$ cat sig | gpgpdump -u
[[Packet]]
  name = "Packet"
  value = "Signature Packet (tag 2)"
  note = "94 bytes"

  [[Packet.Item]]
    name = "Version"
    value = "4"
    note = "new"

  [[Packet.Item]]
    name = "Signiture Type"
    value = "Signature of a canonical text document (0x01)"

  [[Packet.Item]]
    name = "Public-key Algorithm"
    value = "ECDSA public key algorithm (pub 19)"

  [[Packet.Item]]
    name = "Hash Algorithm"
    value = "SHA256 (hash 8)"

  [[Packet.Item]]
    name = "Hashed Subpacket"
    note = "6 bytes"

    [[Packet.Item.Item]]
      name = "Signature Creation Time (sub 2)"
      value = "2015-01-24T02:52:15Z"

  [[Packet.Item]]
    name = "Unhashed Subpacket"
    note = "10 bytes"

    [[Packet.Item.Item]]
      name = "Issuer (sub 16)"
      value = "0x31fbfda95fbbfa18"

  [[Packet.Item]]
    name = "Hash left 2 bytes"
    dump = "36 1f"

  [[Packet.Item]]
    name = "Multi-precision integer"
    note = "ECDSA r (256 bits)"

  [[Packet.Item]]
    name = "Multi-precision integer"
    note = "ECDSA s (252 bits)"

$ cat sig | gpgpdump -j -u
{
  "Packet": [
    {
      "name": "Packet",
      "value": "Signature Packet (tag 2)",
      "note": "94 bytes",
      "Item": [
        {
          "name": "Version",
          "value": "4",
          "note": "new"
        },
        {
          "name": "Signiture Type",
          "value": "Signature of a canonical text document (0x01)"
        },
        {
          "name": "Public-key Algorithm",
          "value": "ECDSA public key algorithm (pub 19)"
        },
        {
          "name": "Hash Algorithm",
          "value": "SHA256 (hash 8)"
        },
        {
          "name": "Hashed Subpacket",
          "note": "6 bytes",
          "Item": [
            {
              "name": "Signature Creation Time (sub 2)",
              "value": "2015-01-24T02:52:15Z"
            }
          ]
        },
        {
          "name": "Unhashed Subpacket",
          "note": "10 bytes",
          "Item": [
            {
              "name": "Issuer (sub 16)",
              "value": "0x31fbfda95fbbfa18"
            }
          ]
        },
        {
          "name": "Hash left 2 bytes",
          "dump": "36 1f"
        },
        {
          "name": "Multi-precision integer",
          "note": "ECDSA r (256 bits)"
        },
        {
          "name": "Multi-precision integer",
          "note": "ECDSA s (252 bits)"
        }
      ]
    }
  ]
}

残りの TODO はこんな感じかな。

  • パケット解析の未テスト部分を埋める(古いフォーマットのパケットのテストどうしよう)
  • Compressed Data Packet (Tag 8) が未実装。どうやって実現しようか悩み中
  • 実は ECC (RFC 6637) がよく分かってない。もしかしたら解釈を間違えているかもしれない
  • 最終的には pgpdump と同等な出力を目指す

余暇でやってるので,まぁボチボチやります。


  1. Go 言語で書いた pgpdump だから gpgpdump。 gpg-pgp-dump という意図もある。 [return]

November 03 2017

ほうじ茶ラテとかはじめてみたよ。ほんのりほうじ茶のテイスト
via Instagram bit.ly/2xQAt1T

November 02 2017

Travis CI でクロス・コンパイル(GoReleaser 編) - golang

以前「Travis CI でクロス・コンパイル」で mitchellh/gox を使ったクロス・コンパイルと tcnksm/ghr を使った GitHub への deploy 手順を紹介したが,これらをまとめてやってくれる GoReleaser というツールがあるらしい。

すでにあるプロジェクトで試すのはどうかと思ったので,まずは以下のデモ用のリポジトリを作って試してみることにした。

ちなみに中身は,このまえついカッとなって作った spiegel-im-spiegel/godump のコードを流用している。 おおっ,役に立ったじゃないか(笑)

GoReleaser の導入

当然ながら GoReleaser 自身は GitHub 上でバイナリを配布しているので,そちらを使うのが手っ取り早い。

ヘルプを見るとこんな感じ。

$ goreleaser help
NAME:
   goreleaser - Deliver Go binaries as fast and easily as possible

USAGE:
   goreleaser.exe [global options] command [command options] [arguments...]

VERSION:
   0.35.5, commit 11fee22a2edf211caec98f2bee97576d3160bdb7, built at 2017-10-25T11:49:23Z

COMMANDS:
     init, i  generate .goreleaser.yml
     help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --config FILE, --file FILE, -c FILE, -f FILE  Load configuration from FILE (default: ".goreleaser.yml")
   --release-notes FILE                          Load custom release notes from a markdown FILE
   --skip-validate                               Skip all the validations against the release
   --skip-publish                                Skip all publishing pipes of the release
   --snapshot                                    Generate an unversioned snapshot release
   --rm-dist                                     Remove ./dist before building
   --parallelism value, -p value                 Amount of builds launch in parallel (default: 4)
   --debug                                       Enable debug mode
   --help, -h                                    show help
   --version, -v                                 print the version

GoReleaser では .goreleaser.yml ファイルでビルドや deploy を制御しているようだ。 goreleaser init コマンドで雛形を生成してくれるみたいなので試してみる。

$ goreleaser init
   • config created; please edit accordingly to your needs file=.goreleaser.yml

作成された .goreleaser.yml の中身はこんな感じ。

project_name: reldemo
release:
  github:
    owner: spiegel-im-spiegel
    name: reldemo
  name_template: '{{.Tag}}'
brew:
  commit_author:
    name: goreleaserbot
    email: goreleaser@carlosbecker.com
  install: bin.install "reldemo"
builds:
- goos:
  - linux
  - darwin
  goarch:
  - amd64
  - "386"
  goarm:
  - "6"
  main: .
  ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
  binary: reldemo
archive:
  format: tar.gz
  name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{
    .Arm }}{{ end }}'
  files:
  - licence*
  - LICENCE*
  - license*
  - LICENSE*
  - readme*
  - README*
  - changelog*
  - CHANGELOG*
snapshot:
  name_template: SNAPSHOT-{{ .Commit }}
checksum:
  name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'

これをベースにアレンジしていくわけだ。

設定ファイルの調整とクロス・コンパイル

さて,修正した .goreleaser.yml がこれ。

project_name: reldemo
release:
  github:
    owner: spiegel-im-spiegel
    name: reldemo

builds:
- goos:
  - linux
  - darwin
  - windows
  goarch:
  - amd64
  - "386"
  - arm
  - arm64
  goarm:
  - "6"
  main: ./cli/reldemo/
  ldflags: -s -w -X github.com/spiegel-im-spiegel/reldemo/cli/reldemo/facade.Version={{ .Version }}
  binary: reldemo

archive:
  format: tar.gz
  format_overrides:
    - goos: windows
      format: zip
  name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
  replacements:
    amd64: 64bit
    386: 32bit
    arm: ARM
    arm64: ARM64
    darwin: macOS
    linux: Linux
    windows: Windows
  files:
  - LICENSE*
  - README*

snapshot:
  name_template: SNAPSHOT-{{ .Commit }}

checksum:
  name_template: '{{ .ProjectName }}_{{ .Version }}_checksums.txt'

主な変更点は以下の通り。

  • brew 項目はバッサリ捨てた
  • コンパイル対象の OS に Windows を加えた。更にアーキテクチャに ARM を加えた
  • ldflags を現状のものに合わせた
  • 圧縮フォーマットで Windows の場合は zip 圧縮にした。また名前の置き換えも行った

これで実際にビルドを行ってみる。

$ goreleaser --snapshot --skip-publish
   • running goreleaser 0.35.5
   • loading config file       file=.goreleaser.yml
   • publishing disabled in snapshot mode
   • SETTING DEFAULTS
   • GETTING AND VALIDATING GIT STATE
   • releasing v0.0.9, commit 6b96405452b3b9af8817157629fce00acb81564e
   • GENERATING CHANGELOG
   • skipped                   reason=not available for snapshots
   • LOADING ENVIRONMENT VARIABLES
   • skipped                   reason=publishing is disabled
   • CHECKING ./DIST
   • BUILDING BINARIES
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit\reldemo
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit\reldemo.exe
   • building                  binary=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit\reldemo.exe
   • CREATING ARCHIVES
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
   • creating                  archive=dist\reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
   • CREATING LINUX PACKAGES WITH FPM
   • skipped                   reason=no output formats configured
   • CREATING LINUX PACKAGES WITH SNAPCRAFT
   • skipped                   reason=no summary nor description were provided
   • CALCULATING CHECKSUMS
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_64bit.tar.gz
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_32bit.tar.gz
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_64bit.tar.gz
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARMv6.tar.gz
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_64bit.zip
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Windows_32bit.zip
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_Linux_ARM64.tar.gz
   • checksumming              file=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_macOS_32bit.tar.gz
   • new release artifact      artifact=reldemo_SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e_checksums.txt
   • CREATING DOCKER IMAGES
   • skipped                   reason=docker section is not configured
   • RELEASING TO GITHUB
   • skipped                   reason=--skip-publish is set
   • CREATING HOMEBREW FORMULA
   • skipped                   reason=--skip-publish is set
   • SUCCESS!

これでビルドと(README.mdLICENSE ファイルを同梱した)圧縮ファイルの生成までできた。 fpm とか snapcraft とか Homebrew とか Docker イメージとか設定がないのでスキップしてるけど,今回はスルーの方向で。

あだバージョンタグを打ってないので --snapshot で。 また現時点では GitHub に deploy して欲しくないので --skip-publish にしている。 OS とアーキテクチャの組み合わせで出来ないものはビルドされないのが分かるだろうか。

実際に生成された実行モジュールを起動してみると

$ reldemo -h
Usage:
  reldemo [flags] [binary file]

Flags:
  -h, --help          help for reldemo
  -n, --name string   value name (default "dumpList")
  -v, --vaersion      output version

$ reldemo -v
reldemo SNAPSHOT-6b96405452b3b9af8817157629fce00acb81564e

という感じでバージョン番号にスナップショット情報が入っているのがわかると思う。

なお GoReleaser でビルドする際は全てのファイルがコミットされている必要がある。 したがって .goreleaser.yml ファイルの調整に手こずるとコミット履歴がアレなことになる。

Travis CI との連携と GitHub への Deploy

Travis CI でクロス・コンパイル」でも書いたが Travis CI から GitHub へ Deploy するためには GitHub のアクセス・トークンを取得して Travis CI の環境変数としてセットする必要がある。

GitHub のアクセス・トークンは “Settings” の “Developer settings” > “Personal access tokens” のページで取得できる。

Get access token in GitHub Get access token in GitHub

repo の権限のみを付けること。 この access token を Travis CI で参照するには, “Settings” の “Environment Variables” でセットすればよい。 Build log にこの access token が表示されないようにすること。

今回の .travis.yml の内容はこんな感じ。`

language: go

go:
  - 1.9.2

install:
  - go get -u github.com/golang/dep/...

script:
  - $GOPATH/bin/dep ensure -v
  - go test -v ./...

after_success:
  - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

最後の行が GoReleaser 起動に関する記述である。 タグが打たれている場合は $TRAVIS_TAG にタグの値が入る。 したがってタグがない場合は GoReleaser は起動しない。

https://git.io/goreleaser は短縮 URL で,中身は goreleaser/getget ファイルでシェル・スクリプトになっている。 こんな感じ。

#!/bin/sh
set -e

TAR_FILE="/tmp/goreleaser.tar.gz"
RELEASES_URL="https://github.com/goreleaser/goreleaser/releases"
test -z "$TMPDIR" && TMPDIR="$(mktemp -d)"

last_version() {
  curl -sL -o /dev/null -w %{url_effective} "$RELEASES_URL/latest" | 
    rev | 
    cut -f1 -d'/'| 
    rev
}

download() {
  test -z "$VERSION" && VERSION="$(last_version)"
  test -z "$VERSION" && {
    echo "Unable to get goreleaser version." >&2
    exit 1
  }
  rm -f "$TAR_FILE"
  curl -s -L -o "$TAR_FILE" \
    "$RELEASES_URL/download/$VERSION/goreleaser_$(uname -s)_$(uname -m).tar.gz"
}

download
tar -xf "$TAR_FILE" -C "$TMPDIR"
"${TMPDIR}/goreleaser" "$@"

要するに GoReleaser の最新バージョンを取ってきて実行しているだけである。

これで全ての準備が整ったので,コミットして origin/master にマージし,タグを討つ。 しばらくして Travis CI 側の処理が終われば Releases ページに反映される。

Release page in GitHub Release page in GitHub

Changelog も GoReleaser が生成している。 コミット・ログを元に生成しているので,ログがショボいと,上の図のように, Changelog もショボくなる。 まぁ,最悪は手直しすればいいんだけどね1

こんなところかな。

.goreleaser.yml が自分の気に入るように調整していく作業は悩ましいが,一度出来てしまえば他プロジェクトでも使い回しがし易いと思う。そうなればリリース管理はかなり楽になるはずである。

ブックマーク


  1. 特定の単語を含むログをフィルタリングする設定も .goreleaser.yml でできる。 [return]
Release page in GitHub

October 31 2017

Hash 値を計算するパッケージを作ってみた - golang

Windows で hash 値を計算するいい感じのツールがない1 ので,もう自分で作っちゃったよ。 といっても自前の部分は殆どないけどね。

詳しくは README.md を見ていただくとして,実際に計算をするのはこの関数。

package hash

import (
    "crypto"
    "io"
    "strings"

    "github.com/pkg/errors"
)

var (
    //ErrNoImplement is error "no implementation"
    ErrNoImplement = errors.New("no implementation")
)

//Value returns hash value string from io.Reader
func Value(r io.Reader, alg crypto.Hash) ([]byte, error) {
    if !alg.Available() {
        return nil, errors.Wrap(ErrNoImplement, "error "+algoString(alg))
    }
    h := alg.New()
    io.Copy(h, r)
    return h.Sum(nil), nil
}

呼び出し側はこんな感じ(空文字列の SHA1 値を取得する場合)。

v, err := hash.Value(bytes.NewBuffer([]byte("")), crypto.SHA1)
if err != nil {
    return
}
fmt.Printf("%x\n", v)
// Output:
// da39a3ee5e6b4b0d3255bfef95601890afd80709

crypto.Hash.New() 関数で hash.Hash のインスタンスを生成している。 hash.Hash はこんな感じの interface 型である。

// Hash is the common interface implemented by all hash functions.
type Hash interface {
    // Write (via the embedded io.Writer interface) adds more data to the running hash.
    // It never returns an error.
    io.Writer

    // Sum appends the current hash to b and returns the resulting slice.
    // It does not change the underlying hash state.
    Sum(b []byte) []byte

    // Reset resets the Hash to its initial state.
    Reset()

    // Size returns the number of bytes Sum will return.
    Size() int

    // BlockSize returns the hash's underlying block size.
    // The Write method must be able to accept any amount
    // of data, but it may operate more efficiently if all writes
    // are a multiple of the block size.
    BlockSize() int
}

つまり,このインタフェースを備えていれば自前の hash アルゴリズムを簡単に組み込むことができるわけだ。 言い方を変えると, hash.Value() 関数で実際に hash 値を計算するにはこのパッケージだけではダメで, hash アルゴリズムを実装するパッケージをインポートする必要がある。

以下にアルゴリズム毎に必要なパッケージを示す。

hash algorithm import package crypto.MD4 golang.org/x/crypto/md4 crypto.MD5 crypto/md5 crypto.SHA1 crypto/sha1 crypto.SHA224 crypto/sha256 crypto.SHA256 crypto/sha256 crypto.SHA384 crypto/sha512 crypto.SHA512 crypto/sha512 crypto.SHA512_224 crypto/sha512 crypto.SHA512_256 crypto/sha512 crypto.RIPEMD160 golang.org/x/crypto/ripemd160 crypto.SHA3_224 golang.org/x/crypto/sha3 crypto.SHA3_256 golang.org/x/crypto/sha3 crypto.SHA3_384 golang.org/x/crypto/sha3 crypto.SHA3_512 golang.org/x/crypto/sha3 crypto.BLAKE2s_256 golang.org/x/crypto/blake2s crypto.BLAKE2b_256 golang.org/x/crypto/blake2b crypto.BLAKE2b_384 golang.org/x/crypto/blake2b crypto.BLAKE2b_512 golang.org/x/crypto/blake2b

この中から必要なパッケージを main パッケージでブランク・インポートする2。 全部インポートするならこんな感じ。

package main

import (
    _ "crypto/md5"
    _ "crypto/sha1"
    _ "crypto/sha256"
    _ "crypto/sha512"

    _ "golang.org/x/crypto/blake2b"
    _ "golang.org/x/crypto/blake2s"
    _ "golang.org/x/crypto/md4"
    _ "golang.org/x/crypto/ripemd160"
    _ "golang.org/x/crypto/sha3"
)

これを CLI (Command-Line Interface) にしたのが以下に示す hash コマンドである。

$ hash -h
Usage:
  hash [flags] [binary file]

Flags:
  -a, --algo string      hash algorithm (default "sha256")
  -c, --compare string   compare hash value
  -h, --help             help for hash

サイズがゼロの空ファイル empty.txt を作って試してみると

$ hash -a sha1 empty.txt
da39a3ee5e6b4b0d3255bfef95601890afd80709

となる。 パイプにも対応してるので

$ cat empty.txt | hash -a sha1
da39a3ee5e6b4b0d3255bfef95601890afd80709

とすることもできる。 さらに -c オプションで hash 値の計算結果をリファレンスの値と比較できる。

$ hash -a sha1 empty.txt -c da39a3ee5e6b4b0d3255bfef95601890afd80709
matched

なお,アルゴリズムには md4, md5, sha1, sha224, sha256, sha384, sha512, sha512/224, sha512/256, ripemd160, sha3-224, sha3-256, sha3-384, sha3-512, blake2s, blake2b/256, blake2b/384, blake2b/512 を指定できるようにした。 既定は sha256

例えば,これで dep の実行モジュールの正当性確認が少し楽になる。 depリリースページで Windows 用の実行モジュールと SHA256 値を記述したファイルをダウンロードする。

  • dep-windows-amd64
  • dep-windows-amd64.sha256

dep-windows-amd64.sha256 の中身が

034f8cf6c225fde51aa025376df12450832f111b39050a7ec451a9ec2ce2cb54  release/dep-windows-amd64

とするなら

$ hash dep-windows-amd64 -c 034f8cf6c225fde51aa025376df12450832f111b39050a7ec451a9ec2ce2cb54
matched

で一発確認できる。 確認できたら dep-windows-amd64dep.exe にリネームして使えばよい。

ブックマーク

参考図書

photo
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan Brian W. Kernighan 柴田 芳樹
丸善出版 2016-06-20
評価

スターティングGo言語 (CodeZine BOOKS) Go言語によるWebアプリケーション開発 Kotlinスタートブック -新しいAndroidプログラミング Docker実戦活用ガイド グッド・マス ギークのための数・論理・計算機科学

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。

reviewed by Spiegel on 2016-07-13 (powered by G-Tools)

photo
暗号技術入門 第3版 秘密の国のアリス
結城 浩
SBクリエイティブ 2015-08-25
評価

自作エミュレータで学ぶx86アーキテクチャ コンピュータが動く仕組みを徹底理解! 数学ガールの秘密ノート/丸い三角関数 数学ガールの秘密ノート/微分を追いかけて 情報セキュリティ白書2015: サイバーセキュリティ新時代:あらゆる変化へ柔軟な対応を 数学ガールの秘密ノート/数列の広場

SHA-3 や Bitcoin/Blockchain など新しい知見や技術要素を大幅追加。暗号技術を使うだけならこれ1冊でとりあえず無問題。

reviewed by Spiegel on 2015-09-20 (powered by G-Tools)


  1. Windows 標準ツールとしては, PowerShell 4.0 以上が入っている PC なら, Get-FileHash コマンドレットが使える。 Windows 7 の場合は “Windows Management Framework 4.0” をインストールすることで PowerShell 4.0 にアップグレードできる。 [return]
  2. ブランク・インポートは main パッケージでしないと golint に怒られるのよ。まぁ言いたいことは分かるけど。 [return]

October 29 2017

イマココ
via Instagram bit.ly/2yXgoZd

Twitter Card メタデータに対応した - remark

なんとなく思いついてこのブログをいわゆる “Twitter Card” に対応させた。

実は Twitter Card が登場し始めたときに Tumblr などで適用していたのだが,あまりに酷い仕様で HTML Validator にかけるとエラーの嵐になるし,そもそも Semantic Web を無視した設計に腹が立って忌避していたのだ。 しかし,まぁ,もう Semantic Web なんか誰も見向きもしなくなってるみたいだし, Web コンテンツを解析する手段は AI 技術を利用したものへシフトしてるようだし,もう(どうでも)いいかな,と。

Twitter Card の仕様も随分シンプルになった。 まず,カードのタイプが以下の4種類のみになった。

Player card は動画やスライドショウのページ用, App card はアプリページ用なので,それ以外の Web ページでは Summary cardSummary with large image を選択することになる。

Summary with large image はアイキャッチ1 用の大きめの画像(300×157から4096×4096)を含む Card で,メディア・サイトなどが多用するあのウザいやつである。 とはいえ,写真やイラストなどを中心としたサイトでは Summary with large image が向いているだろう。

Summary with large image にしないのなら Summary card を選択する。 うちのブログはもちろんオリーブオイル Summary card2

Summary card で必須のメタデータは以下の2つである。 これを指定しないと,そもそもカードが表示されない。

  • twitter:card
  • twitter:title

具体的には <meta> 要素を使って以下のように記述する。

<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Codic API を利用するパッケージを作ってみた — プログラミング言語 Go | text.Baldanders.info">

しかし,このままではページの説明(description)やアイコン画像が表示されないため(何故 optional なのに無理くり表示しようとするのだろう),以下のメタデータも追加する。

  • twitter:description
  • twitter:image

アイコン画像には144×144から4096×4096までのサイズが使える。 これらを合わせると以下の記述が最低限必要と言える。

<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Codic API を利用するパッケージを作ってみた — プログラミング言語 Go | text.Baldanders.info">
<meta name="twitter:description" content="spf13/viper を使ってみたかったのだ。">
<meta name="twitter:image" content="http://text.baldanders.info/images/attention/go-code.png">

以上のメタデータを <head> 要素内に設置する。 これで以下のように表示される(筈)。

Twitter Card: Summary Twitter Card: Summary

以前は twitter:image 等を property-content 属性の組み合わせで記述させようとしていたが(これのせいで HTML Validator がエラーを吐いていた),さすがに改心したようである(笑)

サイトのオーナーやページの作成者が Twitter ユーザの場合は以下のメタデータも使える。

  • twitter:site
  • twitter:creator

これも同じように

<meta name="twitter:site" content="@spiegel_2007">
<meta name="twitter:creator" content="@spiegel_2007">

とすればよい。 カードの見た目には全く関係ないが Twitter アナリティクスか何かで使うのだろう,多分。

メタデータの幾つかは OGP の語彙と置き換えることができる。

一応説明しておくと, OGP は Facebook が最初に考えた仕様で,元々はネット上のコンテンツと Facebook のアプリを関連付けて制御する仕組みだったのだが

Open Graph の相関図 Open Graph の相関図

RDFa の仕様の一部を借用した大変筋の悪いもので,本来の目的は明後日方向に飛んでいき,現在は <head> 要素内にメタデータを記述するための迂遠な手段に堕している3。 ただし OGP で記述したメタデータを参照するサービスは多いため,今だに SEO 対策として用いられているようだ。

Twitter Card のメタデータと置き換え可能な OGP の述語を以下に示す。

Twitter Card OGP twitter:card og:type twitter:description og:description twitter:title og:title twitter:image og:image

これらの述語を既に使っている場合は Twitter Card のメタデータで記述する必要はない。 なお og:type は本来はメディア・タイプ(MIME タイプや RDF/RDFa の語彙で定義されるタイプ)を指定するものなので Twitter Card 用に使うべきではない。

OGP は(一応) RDFa の仕様に従っているのでメタデータ指定には name-content 属性の組み合わせではなく property-content 属性の組み合わせで記述する。 例えば以下の通り4

<head  prefix="og: http://ogp.me/ns#">
  ...
  <meta property="og:title" content="Codic API を利用するパッケージを作ってみた — プログラミング言語 Go | text.Baldanders.info">
  <meta property="og:description" content="spf13/viper を使ってみたかったのだ。">
  <meta property="og:image" content="http://text.baldanders.info/images/attention/go-code.png">
  ...
</head>

<head> 要素に prefix 属性を付けるのを忘れずに(HTML5 の場合)。 なぜ <html> 要素ではなく <head> 要素に付けるかというと, OGP<head> 要素外での使用を考慮していないからである。 まぁ Twitter 側はそんなこと微塵も気にしてないだろうけど。 おそらく接頭辞を og 以外にしたら Twitter 側は認識できなくなるんじゃないのかな(馬鹿らしいので試さない)5

ちなみに <head> 要素内の <title> 要素や description メタデータはまるっと無視するようである。 本当に何でこんな頭の悪い仕様になってるのか知らないが6,最初に書いたように,いまさら Semantic Web なんか気にする人はいないだろうし,どうでもいいか。

ブックマーク

参考図書

photo
セマンティック HTML/XHTML
神崎 正英
毎日コミュニケーションズ 2009-05-28
評価

セマンティック・ウェブのためのRDF/OWL入門 セマンティックWeb プログラミング Linked Data: Webをグローバルなデータ空間にする仕組み オントロジー構築入門 トピックマップ入門 (セマンティック技術シリーズ)

残念ながら紙の本は実質的に絶版なんですよねぇ。是非デジタル化を希望します。

reviewed by Spiegel on 2014/08/17 (powered by G-Tools)


  1. この記事を書くにあたってちょっと調べたのだが「アイキャッチ」というのは和製英語なんだそうだ。ただし,コメントで頂いた情報では “eye-cacher” という言葉はあるらしい(thx!)。なお “eye” は “attention” に置き換えることができるそうで,その場合は “catch the reader’s attention” みたいな言い回しになるとか。ふぅ。英語は難しいぜ。 [return]
  2. 実は一度 Summary with large image を試したのだが,自分で眺めてやっぱりウザかったので Summary card にした。 [return]
  3. RDF/RDFa は Web 上の(URI で記述可能な)あらゆるリソース同士の関係を「主語・述語・目的語」の3つ組(triple)で表すことで machine-understandable な「意味」を与える Semantic Web の実装のひとつである。もちろん Facebook はそんな思想背景など微塵も考慮していなかったと思うが。 [return]
  4. og:image の目的語は URL なので, RDFa 仕様としては property-content 属性ではなく(<link> 要素を使って) rel-resource 属性または rel-href 属性の組み合わせとするのが正しい。のだが, OGP は馬鹿なので property-content 属性とする(つまりリテラル・データとして扱う)よう求めている。私が間違ってるわけではない,決して。ホントなんだよこのクソ仕様は。ブツブツブツ… [return]
  5. ちなみに prefix 属性ではなく vocab 属性を使えば接頭辞が不要になる。どうなるかなんて試さないよ(笑) [return]
  6. ちなみに Facebook は <title> 要素を認識して使っている。はっきり言って <title> 要素と twitter:title メタデータが独立して存在してるってのは詐欺の匂いがするんだが,誰も気にしないのかね。アイキャッチ画像につられて詐欺みたいなサイトに誘導されるってのは Facebook でも見られるが(笑) [return]
Twitter Card: Summary

October 28 2017

イマココ
via Instagram bit.ly/2zN2N5W

October 27 2017

ちょこっと MathJax: インライン数式と別行立て数式 - remark

前回からだいぶ間があいたけど,そろそろ続きを。

  1. ちょこっと MathJax: 初期設定
  2. ちょこっと MathJax: 基本的な数式表現
  3. ちょこっと MathJax: インライン数式と別行立て数式 ← イマココ

インライン数式と別行立て数式

さて,初期設定の回でも少し言及したが,MathJax の数式の表示には2種類ある。 まず本文に埋め込まれる形で表示される数式。 これを「インライン数式(in-line math)」と呼ぶ。 インライン数式は \(...\) で囲んで記述する。

たとえば

エネルギーと質量には \(E=mc^2\) の関係がある。

と記述すると

エネルギーと質量には \(E=mc^2\) の関係がある。

のように表示される。

もうひとつは本文とは行を改めて表示される数式。 これを「別行立て数式(displayed equations)」と呼ぶ1。 別行立て数式は $$...$$ または \[...\] で囲んで記述する。

たとえば

エネルギーと質量には \[E=mc^2\] の関係がある。

と記述すると

エネルギーと質量には \[E=mc^2\] の関係がある。

のように表示される。

$\mathrm{\LaTeX}$ ではインライン数式については \(...\) ではなく $...$ を使う。 なので tex2jax オプションをいじって $...$ を有効にしてしまおう。

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  tex2jax: {
    inlineMath: [['$','$'], ['\\(','\\)']],
    processEscapes: true
  }
});
</script>

オプション設定について詳しくは初期設定の回を参照のこと2

Textstyle と Displaystyle

先程の例で挙げた数式 $E=mc^2$ ではインライン数式と別行立て数式表示に(表示位置以外は)さしたる違いは見られないが,数式によっては表示が明らかに異なる場合がある。 たとえば \sum_{k=1}^n a_k という記述について,インライン数式なら

たとえば $\sum_{k=1}^n a_k$ という記述について

となるが,別行立て数式では

たとえば \[\sum_{k=1}^n a_k\] という記述について

となる。 インライン数式では,できるだけ数式が本文からはみ出ないように自動的に調節してくれているのである。 これを意図的に変えるには \textstyle, \displaystyle, \limits, \nolimits といったコマンドを使う。 以下に各コマンドを使った場合の表示の違いを挙げる。

$\mathrm{TeX}$ 記法 表示 \textstyle\sum_{k=1}^n a_k $\textstyle\sum_{k=1}^n a_k$ \displaystyle\sum_{k=1}^n a_k $\displaystyle\sum_{k=1}^n a_k$ \textstyle\sum\limits_{k=1}^n a_k $\textstyle\sum\limits_{k=1}^n a_k$ \displaystyle\sum\nolimits_{k=1}^n a_k $\displaystyle\sum\nolimits_{k=1}^n a_k$

分数表記

もうひとつ。 インライン数式と別行立て数式で気をつけるべきなのが分数の表記である。 たとえば y=1/x を $\mathrm{TeX}$ 記法で記述する場合は y=\frac{1}{x} と書くが,インライン数式の場合は

たとえば y=1/x は $y=\frac{1}{x}$ と書く

となり,別行立て数式の場合は

たとえば y=1/x は \[y=\frac{1}{x}\] と書く

となる。 しかしインライン数式の場合,これでは文字が小さくなりすぎるし,結局は本文からハミ出してしまう。 なのでインライン数式では \frac を使うのではなくそのまま $y=1/x$ と書くのが良いとされている。

たとえば y=1/x は $y=1/x$ と書く

なお分数でも textstyle と displaystyle に相当する \tfrac\dfrac がある。

$\mathrm{TeX}$ 記法 表示 y=\tfrac{1}{x} $y=\tfrac{1}{x}$ y=\dfrac{1}{x} $y=\dfrac{1}{x}$

ちなみに MathJax でも連分数(\cfrac)が使える3

\[
b_0 + \cfrac{c_1}{b_1 +
  \cfrac{c_2}{b_2 +
  \cfrac{c_3}{b_3 +
  \cfrac{c_4}{b_4 + \cdots}}}}
\]
\[ b_0 + \cfrac{c_1}{b_1 + \cfrac{c_2}{b_2 + \cfrac{c_3}{b_3 + \cfrac{c_4}{b_4 + \cdots}}}} \]

流石にこれはインライン数式じゃ無理だよね(笑)

インライン数式で高さを揃える。

インライン数式では $\sqrt{g}$$\sqrt{h}$ のように数式によって高さが不揃いになるものがある。

インライン数式では $\sqrt{g}$ と $\sqrt{h}$ の高さが不揃い

高さを揃えるには \mathstrut コマンドを使って以下のように記述する。

\mathstrut コマンドを使って $\sqrt{\mathstrut g}$ と $\sqrt{\mathstrut h}$ の高さを揃えてみる

\mathstrut コマンドを使って $\sqrt{\mathstrut g}$ と $\sqrt{\mathstrut h}$ の高さを揃えてみる

さらに \smash コマンドとも組み合わせて

\smash コマンドも使って $\sqrt{\smash[b]{\mathstrut g}}$ と $\sqrt{\smash[b]{\mathstrut h}}$ を揃えてみる

\smash コマンドも使って $\sqrt{\smash[b]{\mathstrut g}}$ と $\sqrt{\smash[b]{\mathstrut h}}$ を揃えてみる

とするともうちょっとだけいい感じになるようである4。 折角なのでマクロに組み込んでしまおう。

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  TeX: {
    Macros: {
      ssqrt: ['\\sqrt{\\smash[b]{\\mathstrut #1}}', 1]
    }
 }
});
</script>

これで以下のように書けば

\smash コマンドも使って $\ssqrt{g}$ と $\ssqrt{h}$ を揃えてみる

同じ結果が得られる。

\smash コマンドも使って $\ssqrt{g}$ と $\ssqrt{h}$ を揃えてみる

別行立て数式に番号を振る

別行立て数式では数式に番号を振ることができる。たとえば \[E=mc^2\] に (a) をいう番号を振りたければ \tag コマンドを使って \[E=mc^2 \tag{a}\] とする。

\[E=mc^2 \tag{a}\]

この番号にはラベル \label を付けて参照することができる。 たとえば \[E=mc^2 \tag{b}\label{eq-b}\] としておいて

\[E=mc^2 \tag{b}\label{eq-b}\]

このラベルを参照するには \eqref{eq-b} でこのように →\eqref{eq-b}← できる($...$ で囲まなくてもよい)5

数式に通し番号を振る

ページ内で通し番号を振りたい場合は,まず TeX オプションで設定を変更する(オプション設定については初期設定の回を参照)。

<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  TeX: {
    equationNumbers: { autoNumber: "AMS" },
 }
});
</script>

実際に番号を振るには数式を \[...\] で囲むのではなく \begin{equation}...\end{equation} で囲む。 自動で番号が振られるので \tag コマンドは不要である。

エネルギーと質量には
\begin{equation}
  E=mc^2 \label{eq-1st}
\end{equation}
の関係がある。
エネルギーと質量には \begin{equation} E=mc^2 \label{eq-1st} \end{equation} の関係がある。

参照も同様に →\eqref{eq-1st}← できる。

余談だが \begin\end で囲まれた領域を $\mathrm{\LaTeX}$ では「環境」と呼ぶ。 \begin{foober}...\end{foober} なら「foobar 環境」と呼んだりする。

equation にアスタリスクを付けた equation* 環境では,環境内の数式を自動採番の対象外にできる6

エネルギーと質量には
\begin{equation*}
  E=mc^2
\end{equation*}
の関係がある。
エネルギーと質量には \begin{equation*} E=mc^2 \end{equation*} の関係がある。

equation 環境以外にも align 環境や gather 環境なども自動採番の対象となる(自動採番を無効にするアスタリスクも有効)。 align 環境などで特定の式に番号を振りたくない場合は \notag コマンドを使って

\begin{align}
  (a+b)^2 &= a^2+2ab+b^2 \\
  (a-b)^2 &= a^2-2ab+b^2  \notag \\
  (a+b)^3 &= a^3+3a^{2}b+3ab^2+b^3
\end{align}
\begin{align} (a+b)^2 &= a^2+2ab+b^2 \\ (a-b)^2 &= a^2-2ab+b^2 \notag \\ (a+b)^3 &= a^3+3a^{2}b+3ab^2+b^3 \end{align}

とすればよい。 また複数の数式を split 環境や aligned 環境で囲むことで複数の数式の塊に一つの番号を振ることもできる7

\begin{equation}
  \begin{split}
    (a+b)^2 &= a^2+2ab+b^2 \\
    (a-b)^2 &= a^2-2ab+b^2 \\
    (a+b)^3 &= a^3+3a^{2}b+3ab^2+b^3
  \end{split}
\end{equation}
\begin{equation} \begin{split} (a+b)^2 &= a^2+2ab+b^2 \\ (a-b)^2 &= a^2-2ab+b^2 \\ (a+b)^3 &= a^3+3a^{2}b+3ab^2+b^3 \end{split} \end{equation}

参考図書

photo
[改訂第7版]LaTeX2ε美文書作成入門
奥村 晴彦 黒木 裕介
技術評論社 2017-01-24
評価

LaTeX2e辞典~用法・用例逆引きリファレンス (DESKTOP REFERENCE) 公共政策入門 ミクロ経済学的アプローチ Rで楽しむ統計 (Wonderful R 1) 岩波データサイエンス Vol.5 数学ガールの秘密ノート/積分を見つめて (数学ガールの秘密ノートシリーズ) 岩波データサイエンス Vol.6 独習 LaTeX2ε 具体例から学ぶ 多様体 【改訂第3版】基礎からわかる情報リテラシー はじめて学ぶリー群 ―線型代数から始めよう

ついに第7版が登場。紙の本で買って常に側に置いておくのが吉。

reviewed by Spiegel on 2017-09-27 (powered by G-Tools)


  1. 「インライン数式」「別行立て数式」という言い回しは『LaTeX2ε美文書作成入門』を踏襲している。ちなみに “in-line math”, “displayed equations” は MathJax のドキュメントでの言い回し。 [return]
  2. 初期設定の回でも言及しているが processEscapes はパラグラフ <p>...</p> 内でのみ有効なようだ。つまり processEscapes が有効な状態では,パラグラフ内の \(...\)\[...\] がエスケープされてしまうので注意すること。 [return]
  3. 出典は『LaTeX2ε美文書作成入門』より。 [return]
  4. これも『LaTeX2ε美文書作成入門』を参考にした。いつもお世話になっています。 [return]
  5. 数式参照用の id は MathJax で動的に生成されているので,ページ外からの参照はお勧めできない。 [return]
  6. 実は \[...\]equation* 環境と等価である。 [return]
  7. split 環境は自動採番の対象外である。 [return]
Older posts are this way If this message doesn't go away, click anywhere on the page to continue loading posts.
Could not load more posts
Maybe Soup is currently being updated? I'll try again automatically in a few seconds...
Just a second, loading more posts...
You've reached the end.

Don't be the product, buy the product!

Schweinderl