愛と勇気と缶ビール

ふしぎとぼくらはなにをしたらよいか

プログラマーは文系の仕事か、理系の仕事か

というような大きく構えたタイトルにしてみたが、デジタルな結論を持った記事ではない。

教育制度として文系とか理系とか分ける意味あんのか、というような議論はさておき、現行でそういう制度が存在している以上は僕の身の周りにも文系学部からプログラマーになった人、理系学部からプログラマーになった人がいて、僕の知る限りでは両者にプログラマーとしての能力の差は見受けられない。

世間では、どうやらプログラムを書くのには数学的な能力が必要だと思われているせいか、あるいはいわゆる情報システム系の学部が理系学部に分類されているせいか、理由は全員に聞いてみたことがあるわけじゃないのでよく分からないが、どうやらプログラマーといえば理系だと思っている人が多いようだ。

僕個人で言うと、大学・大学院と数学の点数の低さを英語の点数でカバーしてきた(これは実際には点数の照会なんてしてないので実際には不明なのだが、明らかに手応えとして英語の方が出来ていたのでこういうことにしている)こと、かつ複数行の数式を繰り返し見ると発狂することなどからいって、自分はどっちかというと文系脳なのだろうと思っている。文系脳という言葉が曖昧なら、能力が言語能力の方に寄っている。その人間がまがりなりにもコードを書いて生計を立てているので、やはり文系理系というのはあまりプログラマーとしての能力に関係がないような気がしている。

とまあ、曖昧な分類の上で人々および個人の曖昧な認識を語るのはあまり意味がないとしても、人間の頭の中の曖昧な分類、曖昧な認識はその人の行動に影響を及ぼす。これは間違いのないところである。

要は何がいいたいかというと、「コードを書く」という営為を「数学的なモデルをコードに落とす」と捉えるか、「誰かに伝わるように文章を書く」と捉えるかで、その人のコードの書き方は影響を受けるのではないか、ということである。

あえて両極端な例を挙げたが、実際にコードを書いた人なら誰でも、コードを書くという営為が(極端な例を除いて)どちらか一方に還元できるものではないことを知っている。コードは数学的なモデルを実現するものでもないし、誰かへの手紙でもない。数学的に正しくても現実界に意味のある影響を与えなければ用をなさないし、読みやすければ効率が悪くてもいいかといえばそうでもない。

汚いコードよりは綺麗なコードが良いが、効率を追求するためには読みやすさを犠牲にしなければいけないこともある。コンパイラがエラーを吐かなくても、人間が読めなければ保守できない。同じ結果をもたらすコードでも、人的ミスを誘発しやすいコードもあれば、そうでないものもある。これら全てはバランスであって、教条的な判断(例えば、メソッドはとにかく短ければ短いほどいいとか)を積み重ねるだけではどうにもならないものである。正しい唯一のコードの書き方なんてものがないのは、正しい唯一の文章なんてものがないのと同じである。同じ目的を達成するにも、千差万別の書き方がある。

微妙に脱線したが、同じコードを書くといっても人ごとに頭の中のものをコードに落とす時のやり方が違うので、コーディングというのは、非常に面白く、また人間的な営為だなあ、と僕は思っているのである。ここまでの流れだと、イマイチ伝わらないかと思うが。

さらに脱線すると、関数型は果たしてオブジェクト指向の次のパラダイムかどうか云々、みたいな議論がたまにあるわけだが、僕は、オブジェクト指向というものが人口に膾炙した理由はそれがプログラミングの技法として適切であるからとかそれを使うとコードの設計がうまくいくとかそういったことが本当の原因なのではなくて、オブジェクトのない世界観(f -> g -> hみたいな関数の連鎖)よりも、オブジェクトのある世界観、つまり「AがBする」「BはCを持つ」みたいな世界観の方が人間の世界の捉え方(もっと大雑把に言ってしまうなら、人間の脳)にマッチしているからなのではないかと思っている。(断っておくがこれは実世界のモデリングが云々とかそういう話とは全然関係がない)要は正しさの問題ではないのである。

(ちなみに、この「オブジェクト指向は人間が認識しやすいのだ」理論は会社の後輩のHaskellerも同じことを言っていた。だから何、という話ではない)

もう一度脱線すると、最近会社でとあるコードを見ていて、「このコード書いた人、迷いがあるな」と思って喋ったら、なぜそのタスクをやるか、最終的な形はどうあるべきか、みたいなことがその人の中ではっきりしていなかった、ということがあった。コードに迷いがあるといっても、コミット数が変に多いとか、表記にゆれがあるとか、動作上問題があるとか、コメントに // TODO // ここどうしたらいいかな? と書いてあるとかそうしたことではなくて、何かよろしくないオーラが出ていた。とりあえず間に合わせようとしている感じ、というか。 僕も30に近づいて、多少エスパーになったのかも知れない。

人間のやることなので、そこに人間が滲み出してくるのは当たり前といえば当たり前なのだが、なんとなく一般通念に反論してみたくて書きました。

2014年にもなってWebページをまともに印刷する方法も知らない俺たちは(あるいは、とあるウェッブエンジニアの闘いの記録)

前略、window.printという関数をJavaScriptから呼び出せば、めでたく印刷ダイアログが表示されて、今目の前に表示されているウェッブページを印刷することが出来るわけだ。

が、とりあえずこの機能をそのまま使うとおかしい。何やら色々おかしい。media="print"なスタイルシートで指定したbackground-colorすら反映されない。「ブラウザの印刷オプションで出来る」という噂もあるが、僕の環境ではちゃんと色が出なかった。

これは一体どうなっているんだ?2014年にもなって、俺達はウェッブページを、自分がいま目の前にしているそのままの姿で印刷することすら出来ないのか?HTML5技術のカンファレンスを開いたり、WebSocketでチャットを作ったりしてる場合じゃない!事件はカンファレンスで起きているんじゃない、俺の家のプリンターの前で起きているんだ!

ゲンよ、画像じゃ。画像を使うんじゃ

というわけで僕はアッと驚くワークアラウンドを探すための旅に出たわけだが、何となくまともな印刷機能を提供していそうなGoogleという会社の、Googleカレンダーというサービスについてさらっと調べてみた。

どうやら、Googleカレンダーは「今見ているウェッブページ」をそのまま印刷するのではなく、一旦画像を生成して、その画像を表示したページを印刷することで回避しているようだ。コードをチラ見したところ、"Print"ボタンを押した際にiframeでアレコレしている節もある。

よし、これだ!画像は印刷でそのまま色が載るのだ。画像なら、スタイルシートがどうのこうのと悩むこともない。我が社もこれで行こう。

f:id:zentoo:20140211154855p:plain

画像をどうやって作るか?

で、だ。画像はどうやって作る?Googleはおそらくカレンダーのデータを画像としてレンダリングするような内部APIを持っているのだろう。これは間違いなくクロスブラウザなソリューションだ。が、自分でそんなものを用意するのは面倒くさくて仕方がない。

要は、今見ているページのDOMを、何らかの形でimgのsrcにぶっ込めるURLに変換できればいいわけだ。よし、ちょっと迂遠だがcanvasでいこう。

html2canvasというライブラリがある。試した所、ある程度まともに動きそうだ。これを使おう。

html2canvasを使って、DOM -> canvas -> Data URIという変換を行う

html2canvasを使うと、以下のような感じでいけるのではないか。

  1. html2canvasで、現在のページのDOM(またはその一部)をcanvas elementに変換する
  2. canvas elementのtoDataURL()でData URIを得る
  3. same originかつ不可視なiframeを開き(または予め開いておき)、その中のimage elementのsrcに親frameから先ほどのData URIをぶっ込む
  4. iframe.contentWindow.print()を呼び出す

完璧じゃないか、我が軍は。

実際のコード

なんとなく、Chrome 33.0.1750.70 betaで動くことは確認した。

    function printWithCanvas() {
        html2canvas(document.getElementsByTagName("main")[0], {
            onrendered: function(canvas) {
                var iframe = document.getElementById("print-iframe");
                var iframeWindow = iframe.contentWindow;
                var imageElement = iframeWindow.document.getElementById("canvas-image");

                if ( typeof canvas.toBlob === "function" ) { 
                    var blobURL = URL.createObjectURL(canvas.toBlob());
                    imageElement.src = blobURL;
                }   
                else {
                    var dataURL = canvas.toDataURL();
                    imageElement.src = dataURL;
                }   

                iframeWindow.print();
            }   
        }); 
    };  

iframeの中味は、以下のような感じのHTMLにしておく。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title></title>
    <style type="text/css">
        #canvas-image {
            width: 100%;
            height: 100%;
        }   
    </style>
    <script type="text/javascript">
    </script>
</head>
<body>
    <img id="canvas-image" src="" alt="" />
</body>
</html>

HTMLCanvasElementのtoBlobがある場合はそれを使う。比較的良心的ブラウザーであるところのFirefoxには既に搭載されているようだ。canvasをimageとして扱う際にData URIにわざわざシリアライズしなくてはいけないのはとっても非効率なので、このAPIはとっととcanvasの載っている全てのブラウザーで実装されてほしい。

canvasがまともに動かないレガシーなブラウザでは、uuCanvasを使えばよいだろう。まだ、試していないが...

まとめ

  • window.printでそのままページを印刷するのは厳しい
  • html2canvasはそれなりに動く
  • ブラウザーはすべからくHTMLCanvasElementのtoBlobを実装すべし

今回は駆け足で考えかつ実装しましたが、もっといい方法があれば教えて頂きたいです。

Martiniでtemplateのデリミタがclient sideのそれと被るのを回避する

最近、「個人プロジェクトだし、別にどの言語で書いてもいいや」というようなものについては試しにgolangで書いてみることが多くなった。単なる素振り。

で、それがWebアプリである時は codegangsta/martini · GitHub を使うわけだが、これに限らずgolangのtemplateではmustache的なアレ ( {{ }} のこと ) がデリミタであるのが基本である。

client sideのtemplateが同様のデリミタを採用していると爆死するので、このように回避する。

    m := martini.Classic()
    m.Use(render.Renderer(render.Options{
        Layout: "layout",
        Delims: render.Delims{"[[", "]]"},
    })) 

martiniを使ってない場合、特に組み込みのtemplateをそのまま使っている場合は、まあドキュメントでも自分で読んでくれい。

Google accountでAuthenticationかましつつAuthorizationもかましたい場合

ハーイ。今日のお兄さんは優しくないので、タイトルの2つを混同してるやつ死刑ね。

Google Accounts Authentication and Authorization — Google Developers

とか

Using OAuth 2.0 for Login (OpenID Connect) - Google Accounts Authentication and Authorization — Google Developers

とかを見てると、「例えばJavaScriptからAPI叩きつつ、裏側でGoogle+ Platform — Google Developers使ってユーザ認証もしておきたい場合はどげんしたらいいの?どれをどう使えばいいの?一体どっちなの?」という疑問に捉われる。

正解はgoogle plus loginの方を使いつつ、gapi.client相当も読み込んでおいて

    <script type="text/javascript">
      (function() {
        var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
        po.src = 'https://apis.google.com/js/client:plusone.js?onload=onLoadCallback';
        var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
      })();
    </script>

API叩く用のscopeも指定しつつAuthorization Request送って(以下はGoogle+ sign-in buttonを使う場合)

    <span id="sign-in">
        <span
            class="g-signin"
            data-callback="signinCallback"
            data-clientid="[YOUR CLIENT ID]"
            data-cookiepolicy="single_host_origin"
            data-scope="openid https://www.googleapis.com/auth/calendar.readonly"
        >
        </span>
    </span>

後は普通にJSからAPIを叩けばオケー。これ以降のユーザ認証についてはドキュメントに書いてある通りなので省略。

        function signinCallback(authResult) {
            console.log(authResult);
            if ( authResult.status.signed_in ) {
                gapi.client.load("calendar", "v3", processCalendar);
            }
            else {
                document.getElementById("sign-in").style.cssText += "display:inline;";
            }
        }

        function processCalendar() {
            gapi.client.calendar.calendarList.list().execute(function(res) {
                console.log(res);
            });
        }

Linux Kernel UpdatesというKindle本がとても良い

Linux Kernel Updates Vol.2013.12

Linux Kernel Updates Vol.2013.12

Linux Kernel Updates Vol.2012.12

Linux Kernel Updates Vol.2012.12

Linux Kernel Updates Vol.2013.08

Linux Kernel Updates Vol.2013.08

このシリーズ、

  • 題名通りの、Linux Kernelの特定versionのupdate内容まとめ
  • 著者自身によるコラム

の2部構成になっているのだが、特に後半部の内容が濃い。

一番上に示したVol.2013.12ではTCP周りのチューニングについて書いてあるのだが、tcp_tw_recycleとtcp_tw_reuseの違いについて日本語で明確に書いてある資料は初めて見た。大抵の人は僕と同様に「えーそうだったんか、知らんかった!」という感想になると思う。

  • 中味ある程度知りたいけど、Kernelのネットワークスタックその他を自分で読み始めるのは色々厳しいな、という人
  • あるいは、自分で読むくらいのモチベーションはあるけど何らかのとっかかりが欲しい人

におすすめ。こういうKindle本が増えていって欲しい。

僕はシリーズ全部を普通に買ってしまったけど、AmazonのPrime会員な人は、オーナーライブラリを利用して読めばお得だろう。

そんじゃーね!