月別アーカイブ: 2014年7月

Let’s Go! #Golang勉強会 Vol.1に参加してきました

勉強会と Co-Edo について

コワーキングスペース茅場町 Co-Edoで行われたGo言語の勉強会Let's Go! #Golang勉強会 Vol.1に参加してきました。Co-Edoは去年はじめてのgit勉強会&もくもく会 に参加させていただいたのが初めてでその後利用していませんでしたが、オーナーの田中さんににはその後Facebookでつながっていただいていたので、もう一度利用したいと思って思っていました。

今日の勉強会は、コワーキングスペースの片隅机1,2卓を囲んで参加者4,5人の小さな勉強会でした。僕以外の人はGo言語経験者みたいです。僕は今日はじめてインストールしました。

Go言語について

Go言語といえば、Googleが作っている新しいプログラミング言語です。コンパイル式でバイナリを出力できるのが特長で、C++をより開発しやすくしたような立ち位置です。そのため、速度を求められる領域での利用が多く、事例としては、Dockerなどがあります。Gunosyのインフラで使用されていることも有名です。僕のTwitterのTimelineの中でもやっている人をちょくちょく見かけるようになりました。

初めて触ったGolangの印象

半日触ってみた印象としては、今からC++を勉強するよりは学びやすい印象を受けました。文法はCやJavaのようでpythonのようなLLの影響も受けている少し独特な記述の言語ですが、使っていればすぐに馴れそうです。

Webフレームワークもありますが、まだ企業で利用するほどには安定していないようです。

しかしライブラリが充実しているので、その気になれば結構良いWebフレームワークやテンプレートエンジンが作れるんじゃないかなと思います。

作ったプログラム(しょぼいので注意)

ひとまず、今回僕はGoを使って簡単なWebアプリを作ってみましたw

URLのパラメータにfrom, toを指定して、任意の範囲でグラフィカルなFizzBuzzができるアプリです。誰の役にもたちません(笑)

普通にやると

$ go run web.go

のようにして、localhost:4000にアクセスして動作を確認できますが、GCE上のDebian(つまり、このブログを動かしているインスタンス)でテストしていたので、4000ポートにアクセスできません。

curlならできますがつまらないですね。

susumuis@instance-4:~$ curl http://localhost:4000/?from=1&to=10
[1] 27781
susumuis@instance-4:~$ <!DOCTYPE html>
<html>
<head>
<title>hello, golang</title>

<style>
    .fizz { color: red; }
    .buzz { color: blue; }
    .fizzbuzz { size: 1.5rem; color: purple; font-weight: bold; }
</style>
</head>
<body>

to value must be integer
</body></html>

なので、一時的にこのブログを配信しているnginxに曲がりをしてインターネットに配信しちゃいましたwww「おまえ、いきなり本番環境に反映する気か!」などのツッコミには屈しません。(もう落としてます)

    location /goweb/ {
        proxy_pass   http://127.0.0.1:4000;
    }

golang - web - fizzbuzz

一応jQueryも入っているのでデバッガで画面いじるのが醍醐味ですw

Goの使いどころ

まだ事例は少ないですが、静的型付け・コンパイルというのがGoの最大の特長でしょう。それでいてpythonなどのライトウェイト言語の特長を取り込んでいるので、非常に良い言語だと思いました。次のような事例では有効でしょう。

  • インフラなどのリソースを極限まで活用したい
  • go言語がインストールされていない環境にアプリケーションを配布したい

ちなみに、

$ go build web.go

のようにしたらバイナリを出力できます。出力されたバイナリはgoがインストールされていない環境でも動作するようです。

susumuis@instance-4:~/godev$ go build web.go
susumuis@instance-4:~/godev$ ls -la | grep web
-rwxr-xr-x 1 susumuis susumuis 5703384  7月 27 21:42 web
-rw-r--r-- 1 susumuis susumuis    1466  7月 27 17:43 web.go

1.4KBのソースコードから吐き出されたバイナリは5.5MBですかwライブラリを含んでるのか大きいですね。フロッピーに入らない。

GoはWebがまだ弱いので、実質的に、静的型付けが欲しいWeb言語という用途ではまだでしょう。その用途ではFacebookが開発したHackが良いと思います。HackはPHPに型を載せたようなシロモノなので、普通にPHPのエコシステムが使えます。パフォーマンスはFacebookがHHVMを開発して頑張っています。さらに極限までチューニングしたい場合は素直にJavaを使うのが現状一番良いでしょう。ただし、Javaを使えば一律に速くなるというわけではなく、JVMという怪獣を手懐ける必要があります。

僕はその必要性を感じませんが、「サーバーもフロントも同じ言語を使いたい」という場合は、現状はNode一択なのでしょう。Dartがどこまで来るのか興味が湧いています。

実はこのブログを動かしているWordPressにHackで書いた独自プラグインを載せています。そんなことも簡単にできちゃいますが、それについては後日書きます。


本番環境でWordPress Themeを安全に開発できる環境を作りました

テスト環境なしの開発はさすがにない

WordPressに移行したこのブログも大分システム・データが安定してきたので、そろそろフロントを調整していくフェーズに入ろうと思います。

ところでWordPressって「外観 > テーマ編集」から直接テーマのphpファイルエディットで来たりするんですが、さすがに怖いっすww

バグったら画面真っ白ですし、下手なセキュリティホール仕込んでしまうかもしれない。当然開発は別の環境でやって、テスト済みのモジュールを本番に反映するっていうのがソフトウェア工学上の基本セオリーです。

そのために自前のMacにもWordPressインストールして、開発環境作ってもいいんですが、それって古いっす!今どきFTPとか情けない。今日パソコンってのはシンクライアントであるべきです。明日このMacが壊れてWindowsに買い替えたとしても、すぐに開発が始められるのが理想です。

概念図

ということで、下記のような環境を作りました。

my-wp-env

データ保全上は完璧ではありませんが、僕がミスっても大丈夫にするためなんで費用対効果からこんな感じにしました。

簡単な解説

Ephemeral DiskとPermenet Disk

Google Compute Engineでサーバを作ると標準で割り当てられるEphemeral Diskは揮発性が高いディスクで、インスタンスがダウンすると消えてしまいます。そこで、データなどは、データ保全性が高いPermanent Diskを追加しこちらに格納することがセオリーです。それぞれ
/
/mnt/pd0
にマウントし、Ephemeral Diskにnginx, hhvm, maria-dbなどのシステムを入れ、pd0にwordpressやmysqlのデータを配置しました。

あとの、細かい説明は割愛w

テーマの編集手順

新しいテーマを作成しようとした場合はこのようにする想定です。

まず、/mnt/pd0/themes/に新しいテーマディレクトリを作成します。おそらく元となるテーマがあるのでコピーして作成します。

$ cp -r twentytwelve susumuis-1

次に、/mnt/pd0/wp-content/themesにシンボリックリンクを作成します

$ ln -s /mnt/pd0/themes/susumuis-1 /mnt/pd0/wp-content/themes/testing

これで準備完了です。ちなみに、/mnt/pd0/wp-content/themes/には実際にテーマディレクトリは配置せず、必ず/mnt/pd0/themes/に実ファイルがあり、/mnt/pd0/wp-content/themes/は常に次のシンボリックリンクを配置します。

production
testing

あとは、productionやtestingの向き先を変えることで、現在のバージョンのテーマを切り替えていくこととします。

テスト環境へのアクセス

標準状態のWordPressでは、テーマは一度に一つしか選択できないので、このままでは本番をproductionにしつつ、testingテーマへアクセスすることができません。

そこで、不本意ですが、プラグインの力を借ります。

Theme Test Drive

というプラグインを使います。このプラグインをインストールすれば、ログイン中の管理者だけ、あるいはURLに?theme=xxx(xxxはテーマ名)を付与したときだけ、別のテーマで表示することができます。

ここで、theme=xxxという指定はプラグインのオリジナルのままでは、一般ユーザーも指定できてしまいます。これでは、開発中の画面を一般ユーザーに晒してしまい、運悪くバグがあると、データを壊すなどの危険があります。

そこで、プラグインを一箇所書き換えました。

theme-test-drive/themedrive.php

@@ -192,6 +192,9 @@

   function themedrive_determine_theme()
   {
+      if (!current_user_can(themedrive_get_level())) {
+          return false;
+      }
       if (!isset($_GET['theme'])) {
           if (!current_user_can(themedrive_get_level())) {
               // not admin

(patchコマンドでパッチできるかはテストしていません)

これで、管理者としてログイン中の時だけ、testingのテーマを試すことができ、一般ユーザーは?theme=xxxと指定しても、テーマを変更することができなくなりました。

テーマの編集をバージョン管理

cd susumuis-1        
git init
git add .
git commit -m 'first commit'

これでとりあえず、バージョン管理できるようにしました。間違えてしまった時はresetで戻ることができます。ある程度できたらcommitしてテストをすることで、「あれ、さっきまではできてたのに」というのを防ぐことができます。必要に応じてリモートリポジトリを作成したりブランチを切ったりしていきたいと思います。

公開

テーマが完成して本番に公開するときはシンボリックリンクを貼り直すだけです。


WordPressに持ってきてしまった、はてなキーワードリンクを解除しました(XMLRPC + wordpress-javaを使用)

前回 はてなダイアリーから持ってきたはてなダイアリーの記事がはてなキーワードリンクを含んでしまっているので、解除するためにスクリプト組むと宣言していました。
今日そのためのスクリプトを書いてリンク解除に成功したので報告します。

変換方法

WordPressのXMLRPC機能を使ってAPI経由でデータ変換をしました。

イメージとしては一つ一つのページを手で開いて、エディタに記事を貼り付けて正規表現置換して保存を繰り返すのと同じです。

この方法のメリットは

  • インターネットに繋がっていればどの端末からも実行できる
  • 履歴データまでは置換しない(WordPressは記事を変更した履歴を保持しています)
  • 変更履歴が残る(その気になればいつでも変換前に戻せる)

デメリットは

  • 前記事分の通信が発生する
  • APIの仕様を調べなければならない
  • APIを有効にしなければならない

当初SQL一本流して変換しようとしましたがMySQLは正規表現で文字列置換をすることができないらしく、それならPHPでスクリプトを組もうとも思ったのですが、DB直接操作するより外部から触ったほうが安全だと考え、API方式を選択しました。

WordPressのAPIはXMLRPC方式と、AtomPub方式の二種類提供されていますが、後者はデフォルトで無効になっていたり、日本語の情報が皆無に近く、XMLRPC方式の場合は、もう少し状況がマシなようで、 wordpress-java というJavaクライアントライブラリもありましたので、今回はこちらを使用しました。

変換プログラム全文

public class App {
    static String user = "******";
    static String pass = "****************";
    static String url = "*************/xmlrpc.php";

    public static void main(String[] args) throws MalformedURLException, XmlRpcFault {
        WordPress wp = new WordPress(user, pass, url);
        List<Page> recentPosts = wp.getRecentPosts(1000);
        Pattern pattern = Pattern.compile("<a class=\"keyword\" href=\"[^\"]+\">([^<]+)</a>");
        for (Page page : recentPosts) {
            String contents = page.getDescription();
            if (contents.contains("keyword")) {
                Matcher m = pattern.matcher(page.getDescription());
                System.out.println(page.getPermaLink());
                while (m.find()) {
                    System.out.println("\t\t" + m.group(1) + "\t" + m.group());
                    contents = contents.replace(m.group(), m.group(1));
                }
                page.setDescription(contents);
                System.out.println("\tsaved status = " + page.getPost_status());
                wp.editPost(page.getPostid(), page, page.getPost_status());
            }
        }
    }
}

実行結果はこんなかんじです。

http://www.susumuis.info/entry/2014/03/31/103829
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        API <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>
        hoge    <a class="keyword" href="http://d.hatena.ne.jp/keyword/hoge">hoge</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        コンストラクタ <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%F3%A5%B9%A5%C8%A5%E9%A5%AF%A5%BF">コンストラクタ</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        Java5   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Java5">Java5</a>
        インスタンス  <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>
        GC  <a class="keyword" href="http://d.hatena.ne.jp/keyword/GC">GC</a>
    saved status = publish
http://www.susumuis.info/entry/2014/03/30/004202
        Mayaa   <a class="keyword" href="http://d.hatena.ne.jp/keyword/Mayaa">Mayaa</a>
続く...

キーワードの変遷を見ると当時どんなことに注目していたのか見られて面白いです。

実行後、変換された記事のリビジョンが1増加しています。差分を表示すると、たしかにキーワードの箇所のみ置換されています。

キーワードだけ置換されている

プログラム解説

解説するまでもない短いプログラムですが、正規表現によって検索をし、置換のところは正規表現を使わずに完全一致で置換しています。こうすることで、どこが変わったのかログに出力することができます。

contents = contents.replace(m.group(), m.group(1));

の部分は記事本文をまるごと置換しているのでメモリ効率の点で言えば有利ではありません。が、以前業務で作った変換ツールはこの方式でトータル数十万行のファイル群を平気で置換できてしまっていたのでこの程度気にするほどではないでしょう。ただし、件数によっては時間がかかるかもしれません。

wp.editPost(page.getPostid(), page, page.getPost_status());

の部分は第三パラメータがJavaDocで

String pubulish : publish status

となっていて、それ以上の説明がありません。WordPressのAPIの仕様書を見てもよくわからなかったので、当てずっぽうでprivateまたはpublicを返すpost.getPost_status()にしたら、うまくいきました。

newPostの場合はbooleanなので、この辺り統一性がないなと思いました。

キーワードリンクの是非について

自分がはてなブログ内にいたら、キーワード被リンクでSEO効果もあったかもしれませんが、はてな外にいますからその恩恵はありません。じゃあ、はてなブログを使用しているならどうかといえば、最近の検索エンジンは外部リンクが多すぎることに対して厳しくなってきていますから、その効果も今はどれほどなのかとも思います。

ユーザーとしてページを見ると、リンクが多すぎるとうるさい感じがしますし、キーワードリンクをクリックして便利だと思ったことはあまりありません。分からない語句があったらはてな内を探すより「ぐぐる」のが自然だと思います。

今後はてなキーワードが終了したり、使用が変わったりすると、外部のブログからのキーワードリンクは全部リンク切れになってしまいますから、引っ越してきたサイトでキーワードリンクが残ってしまっている状況は百害あって一利なしと思います。

実行にJavaが必要ではありますが、同じ状況の方がいらっしゃったら使っていただいて構いません。ただし、事前にバックアップを取るなど自己責任にてお願いします。MySQLのバックアップは

mysqldump -u ユーザー名 -p DB名 > ファイル名.dump

にて取れます (^^;