2014/10/03

KarabinerでMacがメチャ便利になった

Q1: Macの一番気に入っているところは何ですか?
A1: デフォルトでEmacsキーバインドのところです

A2: Macの一番気に入らないところは何ですか?
Q2: デフォルトで中途半端なEmacsキーバインドのところです

_人人人人人人人人人_
> 突然のKarabiner <
 ̄Y^Y^Y^Y^Y^Y^Y^Y ̄

ということでKarabinerを使ってMacのキーバインドを修正したらタイピングパワーが30%ぐらいアップした気がするのでみんなも使うといいよ。

vimの俺には関係ないねって人はKarabinerでキーバインドをvimに変更できるみたいなので価値あるかもです(試してませんが…)

※注意: Emacs/Vi戦争をする場所ではありません

やったこと


Change Keyタブの[Emacs Mode]の項目でいろいろONにした。

Control+YでCommand+Vとか
Control+VでPageDownとか
Control+SでCommand+Fとか
Control+KでCommand+Shift+Right, Command+Xとか

とりあえず、「あぁ、これできなくてイライラしたわー」って項目をONしまくった。
その副作用としてControl+Kでカタカナ変換ができなくなりました(不便 ><)

Emacsと関係ないけど、これメチャ便利


KarabinerのKey Repeatタブで[Delay Until Repeat][Key Repeat]を変更できる。
これメチャ便利。
いままで、
キーリピートに入るのおせーよ!とか思ってたのですよ。
感覚的には「キーを押す→(( あれ? ))→リピート開始」ぐらい。
もう(( あれ? ))時間が織り込み済みになっていた。

[Delay Until Repeat]500msとかに設定されていたのですよ。おクソ様ですよ。
iOSのページ遷移アニメーションも300msぐらい(感覚値)で遅くてイラッとするレベルの人間なので、ここはイッキに100ms!すごく快適!!

ついでに[Key Repeat]、つまりキー連打機能の連打スピードが遅すぎなのですよ。高橋名人レベル(感覚値)。人間の能力以上を発揮してこその機械だろうが。
ということで、これもズバッと33ms。メチャ速くなった!23msとかでもいいかもしれない。

これで、Wordを開いてウトウトしたら「っっっっっっっっっっっっっっっっっっっっっっっk」なんてワードがものすごい勢いで伸びますよw!

おわりに

他の機能は全然触っていないけど、この2つだけ設定するだけもタイピングが気持ちよくなったのでオススメです!


2014/08/08

Google AppEngine/Python入門 - サーバを作ってみよう


Google App Engine(Python)でお手軽にサーバ構築する方法をご紹介します。
(これは東北Tech道場のハンズオンの資料でもあります)

アプリの応用範囲を広げられるように、本家のチュートリアルを構成変更して、実用性を高めています。


#1: Google App Engineとは?


簡単に説明するならGoogleが提供する至れり尽くせりなサーバ環境です。
こんな↓アイコンです。

昔はこんな↓アイコンでした。

むかしむかし、サーバを構築するには
・物理マシンを購入してきて、
・LinuxなどのOSをインストールして、
・データベース・サーバ(MySQLとか)、
・Webサーバ(Apache HTTP Serverとか)、
・開発環境(PHP、Perl、Pythonとか)
などをインストールする必要がありました。
さらに、それらが正しく動くように設定する必要がありました。

こういうのをまとめてLAMP環境(Linux,、Apache, MySQL, P系言語)などと言います。
お金と手間がかかって、サーバを構築するだけで一苦労でした。
多くの人が挫折を繰り返し、
多くの人が残業を繰り返しました。
昔はね。

App Engineはそういうのを初めから全部準備してくれているサービスです。
OS?データベース?Webサーバ?開発環境?全部あるよ、App Engineにね。
(もちろん、これら以外にも沢山のサーバ機能が準備されています。)

#2: でもお高いんでしょ?


ちょっとしか使わないなら無料なので、安心して使いはじめることができます。
たとえばアップロードするファイル容量は合計で1GBまで無料で使えます。
データベースの容量(Datastoreなど)も1GBまで無料で使えます。

無料で使える制限を超えてしまった場合は、クレジットカードを登録しない限りは課金されないので、それも安心。

課金体系はココを参考にして下さい。
英語とドル表記で分かりにくいですが、それほど高くないです。

たとえば、
ファイル容量を1GB追加したかったら月$0.026(3円ぐらい)かかります。
データベースの容量を1GB追加したかったら月$0.18(20円ぐらい)かかります。

安いだけでなく、使えば使うほど、値上がり幅を小さくすると今年の6月ぐらいに発表されました。


値上がり幅の詳細も公表されています。


#3: プログラミング始めたばかりですが…


現時点でApp EngineはPython/Java/Go/PHPの4つの言語が使えます。
Android開発からプログラミングを始めました、という人にとっては安心のJavaが含まれています。

聞いたことのない言語があるかもしれません。
Goとか何?ググったら20億件以上の結果が出てくるんですけど。

プログラミング言語は道具なので、何を使っても良いのですが、道具を色々使えると今後の楽しみが増えるので、今回はPythonを使ってApp Engineのアプリケーション開発の方法を説明します。

人:「おめー、言語、何使ってんよ?」
俺:「Java」
人:「ふーん」
俺:「と、Python」
人:「ぉ、おぉぅ!」
俺:「おめーは?
人:「まぁ、いろいろよ

って感じになれるはずです。
Pythonじゃなくてもいいけど、Pythonもいいよ。Pythonがいいよ。

でも、言語自慢してると、

人2:「おれHaskell」
人3:「おれCOBOL」
人4:「おれBrainF*ck」

などと、ややこしそうな人が寄ってくるのでホドホドに。
(同じ現象がエディタにも起こるので、そちら方面も注意!)

ちなみにPythonのアイコンはこれ↓
ニシキヘビ(Python)です。
これが可愛く見えてきたら立派なパイソニストです。
(パイソニスタとかパイソニアンとかパイソナーと呼ぶ人もいます)
ちなみにJava使いはJavaer(じゃばー)、PHP使いはPHPer(ぺちぱー)とか呼ばれます。Goは?

#4: Google App Engine(Python)をはじめよう!

#4-1: App Engineアプリの開発環境を整えよう

開発を始めるために、SDKをダウンロードします。
ダウンロードページから自分が使っているOSのPython版のSDK(というかランチャー)をダウンロードします。

ここからはMac OS X版をベースに紹介します。
WindowsやLinuxを使っている人もMac版とほぼ同じです。
本家でもインストール手順を紹介しています。
とっても簡単なので参考にして環境構築して下さい。

ダウンロードしてたファイルをダブルクリックするとシンプルなアイコンが登場します。


これをアプリケーションフォルダーにコピーすればインストール完了。


Pythonはversion2.7系が必要です。
Mac OSXの場合ははじめからインストールされています。(たぶん)
Pythonのバージョンを確認しましょう。
まずターミナルを開いて


/usr/bin/env python -V

と入力して下さい。
するとバージョンが表示されます。


2.7.6と表示されたのがバージョン名です。
バージョン名が表示されなかったり、バージョンが2.7.*でなかったら本家からダウンロードしてインストールしましょう。

これでApp Engineを始める準備が完了しました。
Pythonがインストールされている環境であればApp EngineのSDK(ランチャー)をダウンロードするだけです。
これでもうサーバアプリケーションの開発ができるようになりました。

挫折した人は比較的少ないのではないでしょうか?
残業する人もぐっと減ったのではないでしょうか?
お金はまだかかっていません。

#4-2: App Engineアプリを登録する


App Engineのアプリを開発を始めましょう。
アプリはインターネット上に公開されますので、まずはApp Engineアプリを登録する必要があります。

と言ってもとても簡単で、1ステップです。
アプリ登録画面を開きます。


2項目を入力するだけです。

Application Identifierは世界で唯一の名前を指定する必要があります。
Check Availability」ボタンで名前が利用可能か調べましょう。


すでに誰かに使われていたり、使えない名前だと「Sorry,...」と表示されます。
使える名前を探しましょう。

Application Identifierは「Application ID.appspot.com」というサーバのドメイン(ブラウザでアクセスするURL)名にも使われます。
あなたが開発したアプリが公開されるアドレスです。
(独自ドメインを後から設定することもできます)

といっても、気に入らなければ新しいのを作れば良いので、気軽に始めましょう。

あとはApplication Title(アプリのタイトル)を指定して、「Create Application」ボタンをクリックするだけです。
すると「Application Registered Successfully」という味気ないシンプルな画面が表示されたらアプリの登録作業が完了です。


2項目も入力しちゃった!ちょっと休憩しましょう。

#4-3: App Engineのアプリケーションを作ろう


さて、いよいよ、App Engineアプリを開発していきます。
ApplicationフォルダのApp Engineランチャーアイコンをダブルクリック。


ランチャーが起動したら、左下の「+」をクリックしてアプリを追加します。
Application IDに先ほど登録したApplication Identifierの文字をと入力します。
Application Directoryには任意のフォルダを指定します。


文字の入力が完了したら「Create」ボタンをクリックするとアプリのベースが完成です。
指定したフォルダの中にApplication ID名のフォルダができていて、その中に幾つかファイルができていれば成功です。


#4-4: アプリケーションを実行する


実は、この時点でHello world!を表示するアプリがすでに完成しています。
試しにアプリを動かしてみます。

ランチャーにはApplication IDがリスト表示されているので、
それを選択し左上の「Run」ボタンをクリックします。


そうすると開発サーバが自動で起動します。
起動したら「Stop」ボタンと「Browse」ボタンがクリックできるようになるので、
「Browser」ボタンをクリックします。
すると、アプリが実行された画面が表示されます。


勝手にブラウザが開きます。
開いたページは以下のようになります。


Hello world!とだけ表示されました。

#4-5: 挙動の説明するよ


言われるがままにポチポチしてたら何か出た。
という感じだと思うので、ここで少し何がどうなっているのかを説明します。

このアプリの実行に必要なファイルはapp.yamlとmain.pyの2つです。

app.yamlの内容は以下のようになっています。
YAML(ヤムル)という形式の書き方です。

application: helloworld-adamrocker
version: 1
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: main.app

libraries:
- name: webapp2
  version: "2.5.2"

色々設定らしき項目が見えますが、注目は「url」の項目です。
その下に「script: main.app」と書かれています。

これはurlで指定したURLのパス(ここの表記だと全てのパス)にブラウザでアクセスされた時にはmain.pyを実行する、ということを書いています。

次にmain.pyを開いてみましょう。

#!/usr/bin/env python
import webapp2

class MainHandler(webapp2.RequestHandler):
    def get(self):
        self.response.write('Hello world!')

app = webapp2.WSGIApplication([
    ('/', MainHandler)
], debug=True)

実際のファイルには色々とコメントが書かれていますが、プログムで必要な部分はこれだけです。
下から3行分がURLのパスとプログラムを関連付けるところです。

パス「/」に「MainHandler」というクラスが関連付けられています。

つまり、App Engineはapp.yamlを読み取り、ユーザが「/」にアクセスしたらmain.pyを事項し、main.pyの中で「/」に関連付けられているMainHandlerクラスを実行します。

ブラウザを開いてアプリにアクセスする場合、HTTP GETメソッドが使われますので、MainHandlerクラスのgetというメソッドが実際には実行されます。

実行内容はたった1行で

self.response.write('Hello world!')

です。
これがApp Engineのアプリでブラウザに「Hello world!」を表示する仕組みです。
(注:これはApp EngineというよりdjangoというPythonフレームワークの仕組みです)


#4-6: ちょっとカスタマイズしてみよう


「/say」にアクセスした時に、別の文字を表示するようにカスタマイズしてみます。

main.pyにパスとクラスの関連付けを追加します。

app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/say', SayHandler)
], debug=True)

/sayにアクセスした時にSayHandlerクラスが実行されるようにしました。
次にSayHandlerクラスを定義します。

class MainHandler(webapp2.RequestHandler):
    def get(self):
        self.response.write('Hello world!')

class SayHandler(webapp2.RequestHandler):
    def get(self):
        self.response.write('Say hello!')

ちなみにPythonは「{」や「}」のようなコードブロックを明示する構文がありません。その代わりに、スペースやタブの数がコードブロックを表します。なのでPythonのプログラムを書く時やコピペするときはスペースの数を間違えないように気をつけましょう。

ブラウザで/sayにアクセスするとこうなります。


Say hello!が表示されました。
簡単ですね!

では、さっそく、この素晴らしいアプリを全世界に公開してみましょう。

全世界とかコワイ!とかビビらなくても大丈夫です。
どこからもリンク貼られてないので、実質、誰からも見つけられませんw

公開するのも、とても簡単です。
「Deploy」ボタンをクリックするだけ。


クリックするとGoogleアカウントの認証が出てきますので、アカウントとパスワードを入力します。
認証が完了したら、アプリのアップロード(これをデプロイと言います)が開始されます。
デプロイが完了すると、ブラウザで「http://{{ Application ID }}.appspot.com」にアクセスしてみましょう。


ちゃんと表示されました!/sayはどうでしょうか?


完璧です。たった3〜5行ぐらい書いただけでサーバアプリが完成しました。

現在のコードはこんな感じになっています。

#!/usr/bin/env python

import webapp2

class MainHandler(webapp2.RequestHandler):
    def get(self):
        self.response.write('Hello world!')

class SayHandler(BaseHandler):
    def get(self):
        self.response.write('Say hello!')

app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/say', SayHandler),
], debug=True)


#4-7: ログを確認しよう


アプリ開発から少し脱線します。
プログラムが正しく動作しない場合、エラーログを確認して問題の手がかりを得て修正します。
App Engineでもエラーログを確認する方法がちゃんと提供されています。
ランチャーのLogsがそれです。


Logsをクリックするとログコンソールが表示されます。


デバッグする時、ログコンソールに文字を出力したい場合が多々あると思います。
その方法も簡単。以下の様なコードで出力できます。

import logging
class SayHandler(webapp2.RequestHandler):
    def get(self):
        logging.info("============= /say ==============")
        self.response.write('Say hello!')

「/say」にアクセスすると以下のようにログが出力されます。


ちゃんとログが出力されています。
デプロイしたApp Engineアプリのログは、App Engineのコンソールにアクセスし、左にあるメニューの「Logs」で確認できます。


現在App EngineのコンソールはUI改善中のようです。ページの上部に「Try the new Logs Viewer」をクリックすると、下記のような新しいログ表示画面に移動できます。ちょっと見やすくなっています。



#5: HTMLを表示しよう!


Hello world!だけだと味気ないので、HTMLを表示してみましょう。
直接self.response.write()メソッドでHTMLを書いても良いのですが、実用向けじゃないのでJinja2というテンプレートエンジンを使います。

いきなり難しそうですが、たいして難しくありません。
しかも、一度書いてしまえば、ずっと使いまわせるので、この際やっちゃいましょう。

まずはアプリにライブラリを追加します。app.yamlのlibrariesという項目にjinja2を追加して以下のように記述します。2行追加しただけ。

libraries:
- name: webapp2
  version: latest
- name: jinja2
  version: latest

main.pyの先頭にJinja2の設定を記入します。

import os
import jinja2
JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions=['jinja2.ext.autoescape'],
    autoescape=True)

なにやらちょっとややこしそう。。。
いつか、設定を変更したいということがあったら、このプログラムの周辺を調べてみてください。いろいろ設定できて便利です。

今回は、この設定で十分なので、このままコピペしちゃいましょう。
テンプレートでHTMLを表示する時は

template = JINJA_ENVIRONMENT.get_template("file.html")
values = { 'var1': 1 }
self.response.write(template.render(values))
などと、同じことを何度も書くことになるので、使いまわせるようにテンプレートを使うクラスBaseHandlerを定義しておきましょう。

class BaseHandler(webapp2.RequestHandler):
    def render(self, html, values={}):
        template = JINJA_ENVIRONMENT.get_template(html)
        self.response.write(template.render(values))

これで準備完了です。
MainHandlerはwebapp2.RequestHandlerを継承していましたが、先ほど定義したBaseHandlerを継承するように変更します。
そしてHTMLを表示するように変更します。

class MainHandler(BaseHandler):
    def get(self):
        self.render("main.html")

アプリフォルダに以下のmain.htmlを作ります。

<html>
  <body>
    <div>Say hello!</div>
  </body>
</html>


完成です。
アプリフォルダの中身はこんな感じになっています。


10行もプログラムかいちゃいましたね。
さっそく開発サーバでアプリを動かしてみます。


ちゃんと動いていますね!
でも表示が同じなのでHTMLなのかあやしいです。

HTMLを覗いてみるとどうなっているでしょうか?
ブラウザで開いているページを右クリックして「ページのソースを表示」を選択するとHTMLを見ることができます。


これでHTMLを表示させる方法が習得できました!

今現在のソースコードはこんな感じになっていますよ。
#!/usr/bin/env python

import webapp2
import os
import jinja2
import logging

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions=['jinja2.ext.autoescape'],
    autoescape=True)

class BaseHandler(webapp2.RequestHandler):
    def render(self, html, values={}):
        template = JINJA_ENVIRONMENT.get_template(html)
        self.response.write(template.render(values))

class MainHandler(BaseHandler):
    def get(self):
        self.render('main.html')

class SayHandler(webapp2.RequestHandler):
    def get(self):
        logging.info("============= /say ==============")
        self.response.write('Say hello!')

app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/say', SayHandler)
], debug=True)

ちなみに、app.yamlはこんな感じです。
application: helloworld-adamrocker
version: 1
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: main.app

libraries:
- name: webapp2
  version: latest
- name: jinja2
  version: latest

#6: データを保存しよう!


モバイルアプリの場合、サーバをデータの保存、共有目的で使うことが多いと思います。
またWebアプリでも、やはり、データを保存、共有することが多いでしょう。
なので、さっそくデータを保存できるようにしましょう。

#6-1: データ入力フォームを作る


先ほどのmain.htmlを修正してデータを入力できるフォームを作ります。

<html>
  <body>
    <form action="/" method="POST">
      name: <input name="name" type="text" /><br />
      age: <input name="age" type="text" /><br />
      <button type="submit">Save</button>
    </form>
  </body>
</html>

nameに名前を入れ、ageに年齢を入れて保存するだけの投稿フォームです。
methodがPOSTで、actionが「/」になっているので、「/」に対してHTTP POSTリクエストを要求するフォームです。

さっきまではブラウザにURLを直接入力アクセスしていたのでHTTP GETリクエストでしたが、今回はPOSTリクエストを使います。

GETとPOSTリクエストの違いは色々あるのですが、一番わかり易い違いは、URLに表示されるか否かです。
POSTリクエストはURLをブラウザに入力しても表示されませんし、リクエストでサーバに渡すパラメータ(今回だとnameとかage)もURLには表示されません。

#6-2: データを受け取って保存する


サーバ側でのデータ保存にはDatastoreを使います。
画像や大きなファイルを保存するには別の方法(BlobstoreとかGoogle Cloud Storage)を使ったりするので興味があったら発展として調べてみてください。

さっそく何を保存するのかを定義します。
と言っても特別な設定ファイルが必要なわけではなく、プログラム上でクラスを定義するだけです。
今回はnameという文字列とageという数字(整数)を保存します。

from google.appengine.ext import ndb
class UserData(ndb.Model):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)


文字列や整数以外の値を保存したい場合は、本家のPropertiesというところを参考に、必要なデータを追加してみてください。

あとは、「/」にPOSTリクエストが届くはずなので、まずはそのリクエストを受け付けてデータを保存する処理を書きます。

class MainHandler(BaseHandler):
    def get(self):
        self.render('main.html')

    def post(self):
        name = self.request.get('name')
        age_str = self.request.get('age')
        if name is None or age_str is None:
            self.redirect('/')

        user = UserData()
        user.name = name
        user.age = int(age_str)
        user.put()
        self.redirect('/')

MainHandlerにpostというメソッドを追加しています。
これがPOSTリクエストを受け付けた時に実行されるメソッドです。

ここでは、POSTリクエストのnameとageというパラメータを受取り、UserDataのインスタンス(user)の各変数に格納してputメソッドでDatastoreに保存しています。保存が終わったら「/」にリダイレクト(移動)しています。


たったこれだけでサーバにデータを保存できます。簡単ですね。

ただ、これだとデータを保存しただけで、取り出すことができません。
最後に、データを取り出す方法を学んでこのエントリーを終わりにしましょう。

#6-3: データを取り出す


MainHandlerのgetメソッドでデータを取り出せるようにします。
取り出し方もかなり簡単です。

users = UserData.query().order(-UserData.date).fetch(10)

たったこれだけでUserDataに保存したデータを新しい順に10個取り出せます。
fetchメソッドの引数が取り出す個数です。最大1000個まで同時に取り出せます。

orderメソッドは並び順です。
取り出したデータをdataの大きい順に取り出しています。
いわゆる降順と呼ばれる並び順です。

反対にデータの小さい順(昇順)に取り出すには。
order(UserData.date)

と書きます。プラスかマイナスを指定するだけです。

queryメソッドで取り出すデータの条件を指定しています。
今回は何も指定していないので、全てのデータです。
(特定の条件に合致するデータを取得する方法は本エントリー下部の発展#1,2を参考にして下さい)

これをMainHandlerのgetメソッドの中に仕込み、main.htmlの中で表示するようにします。
class MainHandler(BaseHandler):
    def get(self):
        users = UserData.query().order(-UserData.date).fetch(10)
        values = { 'users':users }
        self.render('main.html', values)

<html>
  <body>
    <form action="/" method="POST">
      name: <input name="name" type="text" />
      age: <input name="age" type="text" />
      <button type="submit">Save</button>
    </form>
<hr />
    {% for user in users %}
    <p>{{ user.name }} = {{ user.age }}</p>
    {% endfor %}
</body>
</html>

HTMLの中にHTMLらしからぬコードが入っていますね。
これがテンプレートエンジンの便利なところです。
HTMLを自動で作ってくれるのです。

どういうことかと言うと、
{% for ... %}がプログラムでよく書くfor文ですね。
{% endfor %}までの間を繰り返してくれます。

繰返す内容は
<p>{{ user.name }} = {{ user.age }}</p>
です。
つまり、データベースから取得した最大10個のデータ(users)を一つづつ<p>で囲ったHTMLを作成しています。

テンプレートHTMLにデータを渡す方法はMainHandlerのvalues変数がそれです。
サーバのデータを使って、HTMLが自動で作れるなんて便利ですね!

さぁ、完成です。
開発サーバで「/」にアクセスしてみます。

ちゃんと、保存したデータが表示されました。
これで自分が作ったサーバにデータを入力して取り出せるようになりました!
お疲れ様でした。これでApp Engineを使って自分だけのサーバを作り、公開できるようになりました。

#7: おわりに


最後に、Google App Engineアプリのサーバ上での状況はコンソールから確認できます。
サーバの使用状況、Datastoreの中身、ログを確認したり。
課金もコンソールから設定できます。

ちょっとでも興味を持たれた方は、本家のチュートリアルにチャレンジして下さい。
本エントリにも発展として#1〜#3までを準備していますが、おさらいにもなるので、本家のチュートリアルはオススメです。

さらに、色々なAPIを使う練習ができるトレーニングコースもあります。App Engineで何ができるのかを勉強するにはもってこいです。
もっと発展して勉強したい方は、本家が公開している、サンプルコードを覗いてみても良いでしょう。

では。お疲れ様でした!

最後のソースコードはこんな感じです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import webapp2
import jinja2
import logging
from google.appengine.ext import ndb

JINJA_ENVIRONMENT = jinja2.Environment(
    loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
    extensions=['jinja2.ext.autoescape'],
    autoescape=True)

class UserData(ndb.Model):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

class BaseHandler(webapp2.RequestHandler):
    def render(self, html, values={}):
        template = JINJA_ENVIRONMENT.get_template(html)
        self.response.write(template.render(values))

class MainHandler(BaseHandler):
    def get(self):
        users = UserData.query().order(-UserData.date).fetch(10)
        values = {
            'users':users
        }
        self.render('main.html', values)

    def post(self):
        name = self.request.get('name')
        age_str = self.request.get('age')
        if name is None or age_str is None:
            self.redirect('/')

        user = UserData()
        user.name = name
        user.age = int(age_str)
        user.put()
        self.redirect('/')

class SayHandler(webapp2.RequestHandler):
    def get(self):
        logging.info("============= /say ==============")
        self.response.write('Hello world!')

app = webapp2.WSGIApplication([
    ('/', MainHandler),
    ('/say', SayHandler),
], debug=True)

app.yamlはこんな感じです。
application: helloworld-adamrocker
version: 1
runtime: python27
api_version: 1
threadsafe: yes

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  script: main.app

libraries:
- name: webapp2
  version: latest
- name: jinja2
  version: latest

#発展1: 特定の条件でデータを取得する


Datastoreでデータを取得する方法は以下のコードでした。
users = UserData.query().order(-UserData.date).fetch(10)

queryメソッドに条件を加える事で、特定のデータだけを取り出すことができます。
例えば、ageが18以上のデータを取り題したい場合は
query(UserData.age >= 18)

とします。
ただ、これだとエラーになります。
App EngineのDatastoreの特徴で、2つ以上の変数で並び替えができなくなっています。
変数ageでは並び替えをしていないように見えますが、実は、内部のデータとして、ageで並び替えをしているのです。そのおかげで「>=」という範囲指定のフィルタリングができています。
queryメソッドのageにくわえて、orderメソッドでdataについて並び替えをしていますので、2つ以上の変数で並び替えをしているためエラーになります。

回避方法は簡単です。orderメソッドを外すだけです。
users = UserData.query().fetch(10)

さらに、dataで並び替えしたい場合は、取得したusersを並び替えれるプログラムを組めばOKです。
sorted_users = sorted(users, key=lambda u:u.age)

値が大きい順にソートしたい場合は、
sorted_users = sorted(users, key=lambda u:u.age, reverse=True)

#発展2: 値を指定してデータを取得する


では、ageの値を指定して取得したらどうなるでしょう?
こんな感じ、
query(UserData.age == 18)

これは大丈夫。ちゃんと動きます。
ただし、ちょっと注意が必要です。
アプリを作った時に自動で作られたファイルにindex.yamlがありました。
いままで、なににも使っていなかったのですが、このファイルをおもむろに開いてみましょう
indexes:

- kind: UserData
  properties:
  - name: age
  - name: date
    direction: desc

自動で何かが追加されています。
これはどう読みとくかというと、
UserDataに対して、ageとdataというパラメータで条件をつけてデータを取り出すので予め準備しておいてね。
特にdataは降順(desc)でとり出すからそこんところよろしく。
ということです。
Datastoreの場合は、事前にアクセスする方法をデータベースに教えて必要があります。
ちょっと煩わしいですが、自動で追加されるので、便利ですね。
開発サーバで事項しないとindex.yamlは自動で追加されないので、その時は自分で記入することになります。
自動で追加されるということは、自分の知らない間にドンドン増えていく事になります。
何が起きているかだけでも把握しておくと良いでしょう。

#発展3: Androidからデータを保存する


AndroidでApp Engineのサーバにデータを保存してみましょう。

    private void access() {
        ArrayList params = new ArrayList();
        params.add(new BasicNameValuePair("name", "ADAM"));
        params.add(new BasicNameValuePair("age", "1"));
        HttpPost httpPost = new HttpPost("http://helloworld-adamrocker.appspot.com/");
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        DefaultHttpClient client = new DefaultHttpClient();
        try {
            HttpResponse httpResponse = client.execute(httpPost);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    new AsyncTask() {
        @Override
        protected String doInBackground(String... params) {
            access();
            return null;
        }
    }.execute();

ブラウザで結果を確認してみます。
ちゃんとADAMと1が入っていました!
これでAndroidからクラウドにデータを保存することができました。
これを取り出してアプリに反映させることが出来れば、、、
他の人とデータを共有できれば、、、
アプリの可能性がドンドン広がりそうですね^^


2014/06/23

GoogleのWeb Starter Kitを使ってみる

先日、GoogleからWeb Starter Kitなるものが公開されたので早速使ってみましたのでメモしておきます。

Web Starter Kitのウリは沢山ありますが簡単にまとめると以下の8つです(多い!)

  1. マルチデバイスなボイラープレート式のレスポンシブデザインが使える
  2. ビジュアルデザインシステムでUIをデザインできる
  3. クロスデバイスな同期的な開発ができる
  4. デフォルトでライブリロードしてくれる(コーディングが反映される)
  5. パフォーマンスが最適化される(Minify JS/CSS/HTML)
  6. ビルトインなHTTPサーバが準備されている
  7. Webパフォーマンスの分析を行いレポートしてくれる環境付き
  8. Sassをサポートしている


STEP1: 環境構築
何はともあれ開発環境を整えます。
まずは、Web Starter Kitをダウンロードします。
$ git clone https://github.com/google/web-starter-kit.git
$ cd web-starter-kit/app

次に開発ツールをインストールします。
必要な開発ツールはNode, Ruby, Sass gem, Guplの4つです。
順に入れていきます。(順番大事です)

Nodeはv0.10.x以上が必要です。ターミナルでnode -vを入力してバージョンを確認します。
インストールしていない場合はNode.js本家からインストールして下さい。

Rubyは1.8.7以上のバージョンが必要です。ターミナルでruby -vを入力してバージョンを確認します。さらにgem --versionがエラーなく実行できたらOKです。Rubyのダウンロード&インストールはRuby本家からどうぞ。

次にSassのインストールです。sass -vでバージョンを確認し3.3.x以上であることを確認して下さい。インストール方法はコチラを参照。

最後にGulpのインストールです。Gulpは高速で効率的でシンプルなビルドシステムだそうです。gulp -vでバージョンを確認し、3.5.x以上が必要です。
新規インストールが必要な方は以下のコマンドでインストールできます。
$ sudo npm install --global gulp

これで開発ツールのインストールが完了です。
最後に、これらのツールを使って開発環境を構築します。
Web Starter Kitのディレクトリに移動して依存ライブラリなどをインストールします。
$ cd web-starter-kit
$ sudo npm install
node.jsが全部やってくれるので上のコマンド一発で完了です。便利!

STEP2: とりあえず動かしてみる
まずはプロジェクトをビルドする。
$ cd web-starter-kit/app
$ gulp
このコマンドが成功するとweb-starter-kit/distにビルド結果が格納されます。基本的な開発はこのとおりです。app内を編集してgulpしてdistを見る。
dist内の表示方法はサーバを通して見ると良いでしょう。コレも簡単。
$ cd web-starter-kit
$ gulp serve
これにより勝手にブラウザが起動してhttp://localhost:3000が表示されます。これはdistディレクトリのindex.htmlが表示されています。サーバ起動しておくとweb-starter-kit/app内を監視し、変更あれば先のビルドを自動で行い、ブラウザの表示も自動で変更されます。いわゆるライブリロードというやつです。

表示もちゃんとレスポンシブになっています。
PCサイズのUIデザイン

モバイルサイズのUIデザイン
Chromeの開発ツールでiPhoneをエミュレートしてみた。
iPhoneエミュレートでのUIデザイン
Nexus 5もiPhone5と同じ様なUIデザインになる。
Nexus5もiPhoneと同じUIになる

iPad 3/4も同じ。画面が大きいので2カラムなレイアウトにするとかPCと同じレイアウトでも良いのでは…
iPad 3/4のUIデザインはモバイルと同じ感じ

Nexus7もiPad 3/4と同じ感じだったけど、Nexus10はPCと同じレイアウトになった。
Nexus10はPCのUIと同じデザイン
思い切って古い(失礼!)Androidタブレットだとどうなるか。Motorola Xoomで試してみたら、こちらもPCと同じUIデザインになった。
Motorola XoomによるUIデザイン

STEP3: Web Starter Kitを使って開発してみる
サーバが起動した状態でどんどん開発をすすめます。
web-starter-kit/app内のindex.htmlやらcssなどを修正します。
すると勝手にビルドがはじまり、表示しているページが自動でリロードされ、コーディングした内容がどんどん反映されていくのが分かります。

例えばヘッダーの色を変更してみます。app/styles/main.cssを編集して、保存すると自動でビルドが始まり、すぐに仕上がりが確認できます。
main.cssを変更して開発フローを確認

なお、CSSの記述が規約違反をした場合、gulpがエラーで停止します。その場合、CSSを修正してからgulp serveコマンドで再実行します。

開発ついでに、iPadの画面UIがモバイルと同じくdrawer navigationを利用していたのが微妙だなぁと感じたので、PCと同じUIによせてみます。
やることはとても簡単で、main.cssのメディアクエリー箇所の1200pxとなっているところを1024pxにするだけです。
メディアクエリーの分け方はとてもシンプルで3通りです。

  • デフォルトがモバイル
  • min-device-width: 1200px がラージスクリーン(PCとか)
  • min-device-width:  1200px and max-width: 800px (PCのnarrow表示)

今回はiPadもPC表記にしたいので2つ目のメディアクエリーの1200pxを1024pxにすればOKです。

iPadのUIをPCと同じにした

以上がGoogleのWeb Starter Kitをサクッと使ってみた感じです。もうちょっと使ってみて面白い発見があれば別のエントリーで報告します。

2014/06/18

AppEngine(Python)でDatastoreのユニットテストする方法

AppEngineはプロダクションサーバ(いわゆる運用サーバ)上でDatastoreなどを用いたのテストができません。データを完璧に切り分けて自由にいじり倒してテストできるような環境を作ってくれたりすると嬉しいのですが、そういう感じには進んでいないようです。
ということで、似たような環境をローカルに構築してテストをするしかありません。
今回はその方法をメモしておきます。

STEP1: Datastoreのデータをローカルに構築
前回のエントリーを参考にしてAppEngine上で運用されているDatastoreをダウンロードして、ローカルの開発サーバにアップロードします。一度アップロードしておけば、次からは既にデータが構築された状態になっているので、構築作業は初めの1回だけです。

STEP2(a): Unitテストを記述する
AppEngine/pでUnitテストを書く方法は簡単です。unittest.TestCaseクラスを継承するだけ。
import unittest
from entity import ShopData

class TestShopData(unittest.TestCase):
    def setUp(self):
        #do something before testing each test method

    def tearDown(self):
        # do something after testing each test method

    def test_check_name(self):
        shop = ShopData.all().fileter('email =', 'adamrocker@gmail.com').get()
        self.assertEqual(shop.name, 'adam')

STEP2(b): サンドボックス環境でUnitテストする
AppEngineのUnitテストにはtestbedというサンドボックス環境が提供されています。
testbedを簡単に説明するなら、各テストで副作用の有るような処理(例えばmemcacheの登録・削除など)を各テストが終了した時になかった事にしてくれる便利なテスト環境です。
testbedを使うには以下のようにsetUpで準備しtearDownで後処理を加えます。

import unittest
from google.appengine.ext import testbed
from entity import ShopData

class TestShopData(unittest.TestCase):
    def setUp(self):
        self.testbed = testbed.Testbed()
        self.testbed.activate()
        self.testbed.init_datastore_v3_stub(datastore_file='/tmp/gae.datastore', use_sqlite=True)

    def tearDown(self):
        self.testbed.deactivate()

    def test_put_shop(self):
        shop = ShopData()
        shop.email = 'eve@gmail.com'
        shop.name = 'eve'
        shop.put()
        count = ShopData().all().filter('email =', 'eve@gmail.com').count()
        self.assertEqual(count, 1)

init_datastore_v3_stubメソッドの引数datastore_fileでローカルの開発サーバが保持しているDatastoreのファイルの位置を示しています。
開発サーバを起動するときに指定したオプション(--datastore_path)のファイルパスです。datastore_file引数を持たない場合は何も格納されていないDatastoreがメモリ上に作られます。既存のDatastoreを用いてテストしたい場合はdatastore_fileを利用するとよいでしょう。
UnitテストでShopDataを追加していますが、setUpで設定したtestbedがコレを受け止め、test_put_shopメソッドが終了するとtearDownメソッドでtestbedがdeactivateし、Datastoreの変更がなかったことになります。
なので、test_put_shopテストを何度実行してもTrueになります。

AppEngineのDatastoreをローカルの開発サーバで使う方法

AppEngineのテスト環境を整えている時に欠かせないのがDatastoreなどのテストです。
実行環境と同じような環境をローカルで構築してテストしたいので、すでにオンラインにあるDatastoreを取り出してローカルの開発サーバに入れて試したのでメモしておきます。

STEP1: AppEngineアプリにリモートアクセスできるように設定する
app.yamlに以下のディレクティブを追加する。
builtins:
- remote_api: on
この状態でデプロイしておく。

STEP2: オンラインのDatastoreをダウンロードする
remote_apiがONになっていればダウンロード用のコマンドでDatastoreのデータを取得できるよう。例えば以下のコマンドを実行するだけでOK。
% appcfg.py download_data --kind=ShopData --url=http://{app-id}.appspot.com/_ah/remote_api --filename=ShopData.dump 

{app-id}はAppEngineのアプリIDです。このコマンドによりShopDataというエンティティをダウンロードできます。ローカルにShopData.dumpというファイルで保存され瑠葉に指定していますがファイル名は任意です。
ダウンロードするにはEmailとPasswordが聞かれるのでAppEngineの管理者(or 開発者)アカウントのEmailとPasswordを入力すればOKです。

STEP3: 開発サーバにDatastoreを格納する
こちらもコマンドが準備されているので以下のコマンドを実行するだけ。もちろんローカルサーバが立ち上がっている必要がある。今回は8080ポートで起動しているとします。
% appcfg.py upload_data --url=http://localhost:8080/_ah/remote_api --application=`dev~{app-id}` --filename=ShopData.dump --num_threads=5
--applicationというオプションでapp-idを指定しますが、その前に「dev~」を追加するのをお忘れなく。
アップロードにも同じようにEmailとPasswordが聞かれますのでダウンロードした時のEmailとPasswordを入力します。

これでローカルの開発環境でDatastoreを使えるようになりました。
たとえば、
shop = ShopData.all().filter('email =', 'adamrocker@gmail.com').get()
logging.info(shop.name)
などというプログラムがローカルの開発サーバでも動きます。

2014/06/12

Google AppEngineのアクセスログをBigQueryに流し込む

前回のエントリでBigQueryのStream APIを使ってAppEngineからデータを流し込む事ができたので、AppEngineのリクエストログをBigQueryに流し込んでみます。
これでAppEngineのアクセスをBigQuery基盤でビッグデータ解析してます!と言えますw

STEP0: 事前準備
前回のエントリでBigQueryとAppEngineが連携できている事を前提とします。

STEP1: AppEngineのログを取得する
AppEngine(Python)のLogServiceを使ってリクエストログを抜き取り、BigQuery Stream APIで直接BigQueryにデータを流し込みます。
import time
from google.appengine.api import memcache
from google.appengine.api.logservice import logservice
import httplib2
from apiclient import discovery
from oauth2client import appengine

PROJECT_ID = 'my-project'
SCOPE = 'https://www.googleapis.com/auth/bigquery'
LAST_TIME = 'last_time'

start_time = memcache.get(LAST_TIME)
end_time = time.time()
body = []
for log in logservice.fetch(start_time=start_time,
                                        end_time=end_time,
                                        offset=None,
                                        minimum_log_level=logservice.LOG_LEVEL_INFO,
                                        include_incomplete=True,
                                        include_app_logs=True):
    acc = {}
    acc['ip'] = log.ip
    acc['method'] = log.method
    acc['user_agent'] = log.user_agent
    # add info you need
    body.append( {'json':acc} )

credentials = appengine.AppAssertionCredentials(scope=SCOPE)
http = credentials.authorize(httplib2.Http(memcache))
bigquery = discovery.build('bigquery', 'v2', http=http)
response = bigquery.tabledata().insertAll(projectId=PROJECT_ID,
                                                  datasetId='AppEngine',
                                                  tableId='MyTable',
                                                  body=body).execute()

memcache.set(LAST_TIME, end_time, time=3600)

これで完了。logserviceに関してReferenceを参照するとどういう情報が取得できるのかが分かりますので、必要なデータをBigQueryに流し込むと良いと思います。
start_timeとend_timeは指定しておくと、重複なくアクセスログを回収できると思います。

2014/06/10

Google AppEngineからBigQueryのStream APIにデータを流しこむ方法

BigQueryはビッグデータ解析基盤としてかなり優秀だと思っていました。
ただ、データのアップロードがかなり面倒だった(一度Google Cloud StorageにアップロードしてBigQueryからそのデータを引っ張る)ので二の足を踏んでいました。
最近、Stream APIの制限がかなり緩和されたので、これを期にGoogle AppEngine(Python)と連携させてみた。

STEP1: BigQueryのセットアップ
まずはBigQueryを使える状況にする必要がある。
Google Developers Consoleからプロジェクトを作り、プロジェクトメニューの「APIs」からBigQuery APIをONにする。プロジェクト自体のBilling設定が必要なので注意。といってもかなりの量は無料で使える。

STEP2: クラウドのパーミッション設定
Google Developers Consoleのプロジェクトの「権限(Permissions)」メニューの中の「サービス アカウント」にApp Engineのサービスアカウントを追加する。
AppEngineのサービスアカウントはAppEngineのコンソールの「Application Settings」メニューの中の「Service Account Name」の内容。xxxx@appspot.gserviceaccount.comの様なフォーマットになっているのがそれ。
これで、AppEngineから同一プロジェクトのクラウドサービスへ簡単に(&安全に)接続できるようになります。

STEP3: Google AppEngineにライブラリーをインストール
Google APIを簡単に使うためのライブラリを幾つかインストールしておきます。Pure PythonなのでファイルをコピーするだけでOK。
Downloadはgoogle-api-python-clientからgoogle-api-python-client-1.2.zipをダウンロードしました。
ZIPファイル内の
・apiclientフォルダ
・oauth2clientフォルダ
・uritemplateフォルダ
の3つのフォルダをAppEngineのプロジェクトにコピーします。
私はライブラリ関係をまとめておくため、AppEngineのプロジェクトフォルダ内にlibフォルダを作り、そこに上記3つのフォルダーをコピーしています。

STEP4: AppEngineのからデータをBigQueryに流し込む
BigQueryのStream APIを使う。
Pythonのソースコードは以下の通り
if 'lib' not in sys.path:
    sys.path[0:0] = ['lib']

PROJECT_ID = 'my-project'
SCOPE = 'https://www.googleapis.com/auth/bigquery'

class BqHandler(webapp.RequestHandler):
  def get(self):
    import httplib2
    from apiclient import discovery
    from oauth2client import appengine
    from google.appengine.api import memcache

    credentials = appengine.AppAssertionCredentials(scope=SCOPE)
    http = credentials.authorize(httplib2.Http(memcache))
    bigquery = discovery.build('bigquery', 'v2', http=http)

    body = { 'rows':[{'json':{'id':1, 'name':'Larry'}}, {'json':{'id':2, 'name':'Sergey'}}] }
    response = bigquery.tabledata().insertAll(projectId=PROJECT_ID,
                                                                     datasetId='AppEngine',
                                                                     tableId='MyTable',
                                                                     body=body).execute()

    return

application = webapp.WSGIApplication(
                                     [('/bq', BqHandler)],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == "__main__":
    main()

BigQueryに流し込むデータはbodyです。BigQueryはCSV形式とJSON形式をサポートしていますので、ここではJSON形式にします。
insertAllメソッドでStream APIを使っています。
datasetIdとはデータテーブルを纏める上位構造で、実際のデータが格納されるのはtableIdのテーブルです。

STEP5: BigQueryにDatasetとテーブルを作る
STEP4のプログラムを実行しても、まだBigQuery側に対象となるDatasetとテーブルが存在しないのでデータが保存できません。
そこで、BigQueryのコンソールからまず、DatasetとTableを作ります。

プロジェクト名の右側のプルダウンメニューから「Create new dataset」を選択します。
Dataset IDを「AppEngine」にします。
つぎに作られた「AppEngine」のデータセットの右側のプルダウンメニューから「Create new table」でデータを格納するテーブルを作成します。
「Table ID」を「MyTable」にし、Source FormatをJSONとし、サンプルとなるファイルをアップロードします。
例えば以下の様なJSONファイルをアップロードします。

{"id":0,"name":"test"}

次にテーブルのスキーマを指定します。
今回はidとnameで、それぞれの型が「integer」と「string」なので、以下のように設定します。
id:integer,name:string
これで設定が完了です。アップロードしたJSONファイルと設定したスキーマからテーブルが自動で作成されれば成功です(数十秒かかるようです)。
これでSTEP4のプログラムをAppEngine上で動かせば、bodyの内容がAppEngineデータセットのMyTableにデータが格納されます。
これで、AppEngineからBigQueryに色々なデータを格納できるようになり、そこで解析できるようになりました。

2014/05/25

Google Cloud Platformでdockerに入門する

dockerが何かよく分かってないのでとりあえず使ってみて「コレはなにか?」「何ができるのか?」を実践しながら理解する。

STEP1: Google Compute Engine(GCE)のセットアップ
Google Cloud ConsoleでCloud Projectを作ってCompute EngineをEnableにする。Billingを設定していないとCompute Engineは使えないので注意。
Cloud Projectの設定は
・PROJECT NAME: Docker Test
・PROJECT ID: docker-project
とする。PROJECT IDはデフォルトで設定されている値を使ってもよい。

STEP2: Google Cloud SDKをインストール
% curl https://dl.google.com/dl/cloudsdk/release/install_google_cloud_sdk.bash | bash
% gcloud auth login

ブラウザが開くのでGoogle Compute EngineをセットアップしたGoogleアカウントの認証を行う。
ログインが完了すると。
You are logged in as adamrocker@gmail.com
などとコンソールに表示される。

STEP3: GCEにインスタンスを追加する
ブラウザからGoogle Cloud Console経由でGCEにインスタンスを追加することもできる。GUIがあるので簡単だけど、今後の自動化を考慮してコンソールからCUIでインスタンスを作る。
利用できるインスタンスイメージを知る
% gcutil listimages --project=docker-project

Debianのインスタンスを作る場合、以下のコマンドを実行。
% gcutil addinstance docker-test --image=backports-debian-7-wheezy-v20140415 --project=docker-project

docker-testはインスタン名を意味するので好きな名前を設定してよい。
imageは利用できるインスタンスイメージを指定する。Debian以外にもCentOS,RedHat,SUSEなどが選べる。

CUI内でインスタンスを作るゾーンを選ばされるので、とりえあずネットワーク的に近いasiaにしておく。
Select a zone:
1: asia-east1-a
2: asia-east1-b
3: europe-west1-a  (maintenance starts in 68 days)
4: europe-west1-b
5: us-central1-a
6: us-central1-b
>>> 1
次はマシンタイプを選ばされるので一番安い(しょぼい)n1-standard-1を試す。
Select a machine type:
1: n1-standard-1 1 vCPU, 3.75 GB RAM
2: n1-standard-16 16 vCPUs, 60 GB RAM
3: n1-standard-2 2 vCPUs, 7.5 GB RAM
4: n1-standard-4 4 vCPUs, 15 GB RAM
5: n1-standard-8 8 vCPUs, 30 GB RAM
6: n1-highcpu-16 16 vCPUs, 14.4 GB RAM
7: n1-highcpu-2 2 vCPUs, 1.8 GB RAM
8: n1-highcpu-4 4 vCPUs, 3.6 GB RAM
9: n1-highcpu-8 8 vCPUs, 7.2 GB RAM
10: n1-highmem-16 16 vCPUs, 104 GB RAM
11: n1-highmem-2 2 vCPUs, 13 GB RAM
12: n1-highmem-4 4 vCPUs, 26 GB RAM
13: n1-highmem-8 8 vCPUs, 52 GB RAM
14: f1-micro 1 vCPU (shared physical core) and 0.6 GB RAM
15: g1-small 1 vCPU (shared physical core) and 1.7 GB RAM
>>> 1
十数秒だけ待つと完了する。
確認のため、ブラウザでCloud Consoleを見ると、確かにインスタンスが作られていた。

STEP4: GCE上にdockerをインストールして実行する
まずはSSHでGCEにログイン
% gcutil --project=docker-project ssh docker-test
adam@docker-test:~$

dockerをインストール
adam@docker-test:~$ curl get.docker.io | bash
このコマンドだけで、gitをインストールしたり、dockerのリポジトリとかを設定したり、そこからdockerのイメージをインストールしたり、、、とりえあずdockerを実行するのに必要なツール全部をインストールしてくれる。便利だが何をしているのか謎…。
とりあえずdockerのインストールが完了したので、実行してみる。
adam@docker-test:~$ sudo docker run busybox echo 'docker on GCE!!'
docker on GCE!!
なんか実行できた。
これでdockerコンテナを起動して、その上でechoコマンドを実行。
全コマンドが終了したのでdockerコンテナが終了した。
起動中のdockerコンテナを一覧するコマンドを叩いてみる。
$ sudo docker ps
CONTAINER ID    IMAGE    COMMAND    CREATED    STATUS    PORTS    NAMES

何もリストされていない。dockerコンテナが終了した事が確認できた。
docker runはコンテナプロセスの起動コマンド。
busyboxは起動するコンテナイメージ。
echo 'docker on GCE!!'はbusybox上で実行するコマンド。

試しに別のコマンドを実行してみる。
adam@docker-test:~$ sudo docker run -i -t busybox:latest
# cd /tmp
# echo 'Hello, docker!' > test
# cat test
Hello, docker!
# exit
-iオプションはインタラクションモード。dockerコンテナ内で作業ができる。終わったらexitでホストOSに抜け出す。
-tオプションはタグの指定。イメージ(busybox)のブランチを指定して起動している。
コンテナイメージの/tmpの下にtestというファイルを作ったが、ホストOS上では
adam@docker-test:~$ cat /tmp/test
cat: /tmp/test: No such file or directory
となり、ファイルが読み込めない。
これはコンテナ毎にファイルシステムが独立して管理されているから。
AUFS(Another Union File System)というらしい。
read-onlyでファイルシステム(busybox:latest)を読み込む。
read-onlyで読み込んだファイルシステムに更新が生じたら、
writeできるファイルシステムに当該ファイルをcopyしてwrite。
いわゆるcopy-on-write。
コンテナから見えるファイルシステムは、ベース(read-only)のファイルシステムとwriteできるファイルシステムを透過的に重ね合わせられている。

つまり、/tmp/testファイルはread-onlyのファイルシステムではなくwritableなファイルシステムから読み込んでいるのに対して、それ以外のファイル、例えば/bin/bashなどはread-onlyのファイルシステムから読み込んでいる。

CD(DVD)ブートしたOSと似たようなイメージ。
writableなファイルシステムをメモリ(RAM)上に構築して、OSとして普通に使えるようにしていて、CD(DVD)を取り出したら当然、writeしたファイルも全部消えちゃう。

STEP5: コンテナイメージを作る
コンテナ上でファイルを作ってもコンテナを終了するとファイルもなく(みえなく)なってしまうのでは、dockerを起動するたびにファイルをイチから作らなければならないので面倒だ。
ということで、ファイルを作成したコンテナの状態を新たなコンテナイメージにすれば良い。
言い換えるとbusybox:latestというイメージを起動し、/tmp/testというファイルを作り、それを新たなコンテナイメージ(例えばbusybox2)にする。testというファイルを利用したければbusybox2というコンテナイメージを起動すれば良い。

早速やってみる。
adam@docker-test:~$ sudo docker ps -a
CONTAINER ID        IMAGE                       COMMAND                CREATED             STATUS                         PORTS               NAMES
bfbdf28957c2        busybox:buildroot-2014.02   /bin/sh                4 minutes ago        Exited (127) 2 minutes ago                         prickly_ptolemy   
44449bb3a8e7        busybox:buildroot-2014.02   echo 'docker on GCE!   About an hour ago   Exited (0) About an hour ago                       prickly_tesla       
09ff9045e8e6        busybox:buildroot-2014.02   echo 'Docker has bee   About an hour ago   Exited (0) About an hour ago                       naughty_torvalds   
実は、いままでdockerコンテナを起動しread-write系のコマンドを実行していたものは全て別コンテナとして存在している。
つまりbusyboxイメージのブランチイメージとして存在する。docker runコマンドを打つたびにあらたなコンテナをドンドン作っていたことになる。
ということなので、testファイルを作ったコンテナ(ID: bfbdf28957c2)をコンテナイメージとして登録してみる。
adam@docker-test:~$ sudo docker commit bfbdf28957c2 busybox:busybox2
利用できるコンテナイメージの一覧を見る。
adam@docker-test:~$ sudo docker images
REPOSITORY          TAG                   IMAGE ID            CREATED             VIRTUAL SIZE
busybox             busybox2              1772b0d9129c        8 seconds ago       2.433 MB
busybox             latest                4c0f792ebd1e        29 hours ago        2.433 MB
早速、busybox2を起動してみる。
adam@docker-test:~$ sudo docker run -i -t busybox:busybox2
# cat /tmp/test
Hello, world!
ちゃんと/tmp/testファイルが残っていた。
コレができるようになると、コンテナイメージをどんどん作れる。試しに作ってみて、上手くいかなかったら消す。上手くいったところまでをcommitしてセーブしておく。システム構築がgitでソース管理するかのように気軽にできるようになる。

ちなみにコンテナを削除するには
$ sudo docker rm <CONTAINER ID>
まとめて削除するには
$ sudo docker rm `sudo docker ps -a -q`

STEP5: dockerをOS起動時から使えるようにしておく
おまけとして、dockerデーモンを起動スクリプトに追加しておき、OS起動時からdockerが使えるようにしておく。
$ sudo update-rc.d docker defaults


参考:
docker.io - Google Cloud Platform
Dockerを支える技術
GoogleのDockerレポジトリ

2014/05/22

User Timing APIを使ってWebアプリケーションの性能を測定する

Webアプリを作っていてチューニングしたいんだけど性能測定が難しかった。
ChromeのProfileを使ったり、console.timeを使ったりしてみたけど、User Timing APIという便利なAPIがあるらしいのでそれを使ってみた。

■環境
Chrome 35.0.1916.99 beta
※サポート外のブラウザで動作させたい場合のPolyfill(機能差分を埋めてくれるライブラリ)が公開されているのでそれを使うと良いそう。

■参考
User Timing API あなたの Web アプリをもっと理解するために

■仕込み
性能測定に使う主なメソッドは以下だけ。
window.performance.mark(...)
測定したいポイントにこのマークを設置する。 例えば、
window.performance.mark('load_file_start');
...
window.performance.mark('analyze_file_start');
...
window.performance.mark('load_file_end');
...
window.performance.mark('analyze_file_end')
のようにmarkをどんどん設置していく。

■測定
マーク間の時間を測定したい場合にmeasureメソッドを使う。
window.performance.measure('load_file', 'load_file_start', 'load_file_end');
第一引数は測定値に対する名前なので分かりやすい名前を付ける。
第二引数は測定の開始点のマーク名。
第三引数は終点のマーク名。
測定値はwindow.performanceが保持しているので、値の取得は
var items = window.performance.getEntriesByName('load_file');
などとする。
ByNameの他にByTypeなどもある。
実際の数値の出力は
for ( var i = 0; i  < items.length(); i++ )
  console.log( items[i].name + ':' + items[i].duration + ' ms' );
などとする。

■ちょっと便利なマーク値
User Timing APIが便利なのは事前に幾つかマークが仕込まれていること。
例えば、
HTMLの読込を開始する時は'fetchStart'
DOMの読込が完了した時は'domComplete'
完全にHTMLの読込・解析が完了した時は'loadEventEnd'
など。
全内容はコチラを参照→W3C Navigation Timing 
ユーザがJSで仕込める以外の部分にもマークが仕込まれているのが便利。

DOMの読込が完了してからHTMLの全ロードが完了するまでの時間を測定したい場合は。
window.performance.measure('afer_dom', 'domComplete', 'loadEventEnd');
とすればマークを何も設置せずに測定できる。
便利!

2014/05/21

Bloggerで簡単にシンタックスハイライトする方法

google-code-prettifyというライブラリを使うのがとてもラクだと思うので、これを導入します。

あとビジュアル的にシンプルなので好み。
行番号を表示したい場合は別のライブラリを使うことをオススメ。
SyntaxHighlighterとか。

テンプレートの「HTMLの編集」で<body>タグの閉じる直前に以下のJSを読み込む。
JSはCDNを利用させてもらうことで更にラクします。
<script type="text/javascript" src="http://cdn.jpillora.com/js/jp-prettify.js"></script>
投稿内でコードを表示したい部分を
<pre class="prettyprint">
... {{ some codes }} ...
</pre>
という具合に囲む。
{{ some codes }}の中はHTML特殊文字エスケープする事を忘れずに。

Polymer.jsを使ってWeb Componentsを試す

Webフロントまわりの開発をやっているとサーバサイドに比べてあまりにも面倒。調べているとWeb Componentsがかなり便利そうだったので調べたものをメモ。

■環境
Google Chrome 35.0.1916.99 beta

■参考ページ
Web Components普及の夜明け!?Polymer.jsを試してみた。
なぜWeb Componentsはウェブ開発に革命を起こすのか

■ライブラリを整える
ライブラリをGitHubから取得する。
まずブラウザの機能補完(Polyfill)ライブラリを取得する
%git clone https://github.com/Polymer/platform.git
次にWeb Componentsの実装Polymer.jsを取得する
%git clone git://github.com/Polymer/polymer.git
独自にコンポーネントを作ることもできるけど、今回は簡単に試すために既にあるパッケージ群(Toolkit-UI)をゲットしておく。
%git clone https://github.com/Polymer/toolkit-ui.git

■ページを作る
Web Componentsを使った簡単なHTML(index.html)をつくる(参考)。
<!DOCTYPE html>
<html>
  <head>
    <script src="polymer/platform.js"></script>
    <script src="polymer/polymer.js"></script>
    <link rel="import" href="toolkit-ui/elements/g-icon.html">
  </head>
  <body>
    <g-icon src="http://www.adamrocker.com/blog/images/category_7.jpg"></g-icon>
    <img src="http://www.adamrocker.com/blog/images/category_7.jpg" id="icon">
  </body>
</html>

<link>タグでg-icon.htmlコンポーネントを読み込む。
<g-icon>はToolkit-UIの中で独自に作られたタグ。このタグを使うとHTML/CSS/JSのパッケージが展開されたかのうように表示される。
Custom Elements/Shadow DOM/Templateあたりの要素技術を使っている。

■ブラウザで表示する
index.htmlをそのままブラウザで開いても何も起きない。
<link> タグのimportがローカルファイルに対応していないのでg-icon.htmlファイルを読み込めないため。
なので以下のコマンドでローカルに簡単なWebサーバを立てる。
コマンドはindex.htmlファイルと同階層で実行する。
%python -m SimpleHTTPServer

ブラウザでhttp://0.0.0.0:8000/index.htmlにアクセスすればアイコンが2つ表示されるはず。1つは24px角の小さなアイコンで、これはg-iconが以下のように展開されている。
<style>
      @host {
        * {
          display: inline-block;
          vertical-align: middle;
        }
      }

      #icon {
        width: 24px;
        height: 24px;
        cursor: pointer;
        background-repeat: no-repeat;
        background-position: center;
        background-size: 100% 100%;
      }
</style>
<div id="icon" style="background-image:url(http://www.adamrocker.com/blog/images/category_7.jpg); width:24px; height:24px">
</div>

<g-icon>が展開されると<div>要素のidがiconだが、index.htmlの<img>要素のidもicon。バッティングしてアウトな感じだが、Shadow DOM内はカプセル化されているので実際は<g-icon>が展開されたidはindex.html内のidに影響しない。当然classとかも。素晴らしい!
Web Componentsが広まれば<link> タグでコンポーネントをimportするだけでWebページの部品が使える世界になる。

  ■Web Componentsとは
HTML/CSS/JSのかたまり。ボタンやアラートやカレンダー、カラーピッカーなど、HTML/CSS/JSのセットで使いまわされるであろう機能部品を1つのかたまりとしてパッケージ化するブラウザ技術。
標準化に向けたW3Cのワーキングドラフト→Introduction to Web Components

Web Componentsはいくつかの要素技術が統合されている。
Template
Mutation Observer
HTML Imports
Custom Elements
Shadow DOM
Object.observe()
Pointer Events
Pointer Gestures
Web Animations

ブラウザネイティブでサポートしているのはごく一部の要素技術だけ。
Chromeでも全ての要素技術をサポートしているわけではない。

■Polymer.jsとは
Web Componentsを使えるようにするJavascriptライブラリ。
ブラウザの差分を埋める(Polyfill)ためにplatform.jsと併用して使う。
それによりWeb Componentsの要素技術が使える数とブラウザが飛躍的に増える。
ブラウザの互換性一覧はコレ→Browser Compatibility

■jQueryと違うのか?
考え方のオリジナルはjQueryっぽい。jQueryが成功しているので足りない部分を補完して、完成度の高いブラウザ標準機能にしようとしているみたい。
足りない部分とは、例えば、jQueryプラグインはHTMLをセットで動く事が多いけどjQueryプラグインの内部にHTMLを加えるには、DOMを作る要領で作ることはできるが…面倒。CSSも加えるとなると更に面倒。そしてクラス名などが他のライブラリとバッティングする事も考える必要が出てくる。そこらへんをまとめてWeb Componentsが面倒を見てくれる。

例えばHTML Importsを使えばHTML/CSS/JSのまとまりを読み込めるので、HTMLの生成をJSで行う必要がなく簡単。

Templateを使えば、HTMLの生成が飛躍的に楽になる。Template技術はサーバサイドでのHTML生成では当たり前に使われている。これは、そもそもWebフロント領域の話なので、これがHTMLだけで何とかなるようになると仕事がめっちゃ楽になる。いちいちサーバサイドのプログラム(JSPとかPHP、Python、Ruby)を弄る必要がなくなる。

Shadow DOMを使えば、HTML/CSS/JSのまとまりをカプセル化できるので、名称の汚染がない。CSSのクラス名などがHTML/CSS/JSのまとまりの中でしか適用されないので他のライブラリやコンポーネントのことを意識する必要がない。

2014/05/20

Chromeを使ってJavascriptの性能測定をする方法

いろいろライブラリを使えば細かくできるとは思うけど、サクッとザックリ簡単にやりたい場合。
console.time('NAME')
doSomething();
console.timeEnd('NAME')
'NAME'で挟んだ部分の処理の時間を
NAME: 345.321ms
のように出力してくれる。
Firefoxでも同じ方法でできるらしいけど未確認。

Chromeの場合、DevToolsのProfilesタブを使えば、もっと詳細にプロファイルできる。
あくまでconsole.timeはサクッとザックリ。

2014/05/19

Pythonの標準デバッガpdbの使い方

コマンドラインからPythonの標準デバッガ「pdb」を使うときに忘れがちなのでメモ。よく使うデバッガコマンドだけ。
(PyCharmとかのIDEを使えばもっと簡単に使える)

■コマンド
% python -m pdb test.py
test.pyをデバッグ実行できる

■pdbのデバッガコマンド

l
list。実行中のプログラムの周囲11行を表示。第一引数で数字を指定した場合は、その行の周囲11行を表示。例えば「l 12 」。指定した範囲のプログラムリストを表示したい場合は「l 10, 40」とする。PC(プログラムカウンター)がどこにあるかも確認できるしブレイクポイントを貼るときの行数の確認にもなるのでまずはコレ。

b
ブレイクポイントを指定。 「b 10」で10行目にブレイクポイントを貼る。ファイル名を指定したい場合は「b test.py:10」。行番号を指定しない場合はブレイクポイントの一覧表示。

cl
clear。指定したブレイクポイントを解除。指定方法はブレイクポイントと同じ。

s
Step Into。中へ中へと進む。

n
Step Over。nextの意味らしい。関数呼出しは中にはいらない。

r
return。現在の関数が返るまで実行を継続。

c
continue。次のブレイクポイントまで実行。

p
PCのスコープ内でアクセスできる式を評価する。「p total」とすると変数totalの値の中身を見ることができる。

q
quit。デバッガを終了する。

2014/05/16

Chrome CanaryでBeacon APIを試す

■ビーコンAPIとは
Analyticsなどで細々したデータをサーバに送る場合、ブラウザ(ページ)をブロックせずに送信するメカニズム。バックグラウンドで送信してくれる。もうsync-XHRとか必要なくなる。

仕様:W3C
Firefox実装:Bugzilla 936340

■実験環境
クライアント:Google Chrome Canary (バージョン 37.0.1995.0 canary)
サーバ:RequesetBinでリクエストを監視。
     (ログインしておけばRequestの内容をキャプチャできるサービス)

■実験
Chrome Canaryのターミナルで以下を打つべし
> navigator.sendBeacon('http://requestb.in/1crhlwy1', 'test')

RequestBinの結果は以下のとおり

■注意
RequestBinから何も返さないのでChromeのNetwork Statusでは(canceled)になって届いていないように見える罠に注意。

■Source
https://plus.google.com/+agektmr/posts/8YDy7pAcaBn

Macで単語辞書を引くショートカット

よく忘れるのでメモ。

[command] + [control] + [D]


2014/05/14

Google ChromeでChrome for Androidで表示中のサイトをデバッグする

環境
・Android4.2以上
・Chrome for Android 32以上
・Androidの設定でUSB debuggingがONになっていること

準備
Google Chromeでchrome://inspect/#devicesを開き
左メニューにある「Devices」を開いて「Discover USB devices」のチェックボックスがONになっていることを確認

接続
AndroidをUSBに接続し「Allow USB debugging?」のダイアログで「OK」をタップすると、chrome://inspect/#devicesにChrome for Androidのタブ情報が表示されて「inspect」でDOM要素の検査ができる。


Google Cloud StorageのバケットにCORSを設定する方法

別ドメインからアクセスするとAccess-Control-Allow-Originというエラーがでるので、それを回避するためにGCSのBucketにCross-Origin Resource Sharing(CORS)を設定する。

事前準備
gsutilが未インストールならGoogle Cloud SDKを突っ込んでおけばOK。

事前確認
設定されているCORSを確認する
$ gsutil cors get gs://BUCKET-NAME

STEP1: CORS.xmlを作る
ローカルにcors.xmlファイルをテキトウに作り、CORSの設定をcors.xmlに記入する。
<?xml version="1.0" encoding="UTF-8"?>
<CorsConfig>
  <Cors>
        <Origins>
            <Origin>http://*.adamrocker.com</Origin>
        </Origins>
        <Methods>
            <Method>GET</Method>
            <Method>POST</Method>
            <Method>HEAD</Method>
        </Methods>
        <ResponseHeaders>
            <ResponseHeader>*</ResponseHeader>
        </ResponseHeaders>
        <MaxAgeSec>86400</MaxAgeSec>
    </Cors>
</CorsConfig>
Originタグでアクセス元のURLを指定する。例えば「http://www.adamrocker.com」。
Methodタグは許可するHTTPメソッド。
MaxAgeSecはブラウザキャッシュの時間。

STEP2: CORSをGCSのバケットに設定する
$ gsutil cors set cors.xml gs://BUCKET-NAME
設定が上手くいっているか確認。
$ gsutil cors get gs://BUCKET-NAME