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に色々なデータを格納できるようになり、そこで解析できるようになりました。