2012年6月5日火曜日

GAE/Gで時間のチェック(Datastore編)

GAE/GのSDK 1.6.4からGo Ver 1が適用され、
時間をデータストアに保存する時は、datastore.Time(int64のtype。今は削除されている)では無く、time.Timeが利用できるようになっています。(参考Blog記事

time.Timeをデータストアに格納しようと思うと、普通に日時が設定されていれば問題ないのですが、
例えば、データストアのプロパティの追加した時など、日時のプロパティに時間が設定されていない時があります。

日時が設定されていないまま、Putを行うと、"time value out of range"のエラーが
発生してしまいます。

これを回避するためには、

  1. PropertyLoadSaverのLoadかSaveで日付をセットする。
  2. リリース前にデータの移行をしておく。(未設定の日時が発生しないようにしておく)
のどちらかになると思います。2のケースだと全件Putされるので問題ないと思いますが、
移行プログラムでデータを移行させない事の方が多いと思います。

したがって、PropertyLoadSaverインターフェイスを実装して、以下のように、
時間がZeroかどうかを判定するロジックを入れておかないと、Putが成功しません。
判定メソッドはGo言語標準で用意されているIsZero()を利用します。
LoadとSaveどちらで設定するのも構わないと思いますが、ここでは、Load時に設定する
方針にしてみます。(if文1つなら処理コストはあまり変わらないだろうと言っても、読み込み時に毎回実行するのは無駄だ。と思われる場合はSaveで実装するのが良いでしょう)

func(hoge *Hoge) Load(c <-chan datastore.Property) error {
        // データストアから読み出し。
 if err := datastore.LoadStruct(hoge, c); err != nil {
  return err
 }

 if hoge.Date.IsZero() {
  // データがない(0000/01/01状態)ものは現在時刻にするなど。
  hoge.Date = time.Now().Format(TimeFormat)
 }

 return nil
}

データが既に入っている場合はIsZero()がfalseになると思われるので、
特に時間の設定は不要(データストアから読みだした時点で代入されている)でしょう。


GAE/GでバッチGetをした時の注意点

最近、Androidアプリ「ええことなかったわー(β)」を開発したのですが、
実は、データを貯めているサーバ側はGAE/Gで開発しています。

本当に、レスポンスとしては早いですよ。
今のところ、リクエストに対するレスポンスは、
ms単位の時間でレスポンスを返しています。
(GAE/Jでも仕事でサービスを利用していますが、レスポンスの内容が全然違いますが、1s前後のオーダだったりするので…)

先ほど、Admin ConsoleでのPerformanceを最弱にしていなかったので、
まだユーザ数も少ないので、最弱(Max Idle Instance = 1+Min Pending Latency = automatic)にしてみました。その結果は後日…ということで。

さて、本題のバッチGetを利用するときの注意点なのですが、
結論から言うと、「指定したキーのエンティティが1件でも無いと、appengine.MultiErrorを返す」ということです。
実際には、以下のようなコードになると思いますが

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
// 何かのエラー
}

全てのキーのエンティティがあれば、エラーの判定にならない(戻り値=nil)なのですが、
1つでもエンティティが存在しない場合、全てエラー(戻り値 = appengine.MultiError)となります。
(おかげで、全部エラーレスポンスに…)

で、これは困ったという事で、以下のようなチェックを行うことになります。

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
 if _, ok := err.(appengine.MultiError); ok {
  // バッチGetだと指定したキーのエンティティが1個でもない場合、MultiErrorが返る。
  c.Infof("Hoge NoSuchEntity")
  return keys, data, nil
 } else {
  c.Errorf("Batch Get Error %s", err.Error())
  return keys, nil, err   
 }
}

実際、appengine.MultiErrorが返されても、存在しているデータは取得できるので
取得したデータが構造体にセットされている部分と、空の部分がsliceに存在する
状態になるので、後で、空の部分を除外するなど、編集処理が必要となります。

ちなみに、appengine.MultiErrorはtype MultiError []errorと定義されていますので
複数のエラーが含まれた状態になることも考慮しておく方が良いかもしれません。
私はGetだけの利用だったので、今のところ、正常なものは利用しています。

MultiErrorの戻り値はGetMulti/PutMulti/DeleteMultiで戻される事があるという
ことなので、(ドキュメントのBasic Operationsに明記)利用するときはappengine.MultiErrorの中身を確認してみてください。

2012年5月15日火曜日

BloggerのRSSフィード

本当はこの記事を書く予定は無かったのですが、
ふと思い出したので調べてみました。

基本的にBloggerのRSSフィードは
Atom1.0: http://blogname.blogspot.jp/feeds/posts/default
RSS 2.0: http://blogname.blogspot.jp/feeds/posts/default?alt=rss

このサイトのフィードだと、「http://takashi-yokoyama.blogspot.jp/feeds/posts/default」という感じです。

で、取得できます。
(ちなみに最後に添付しているヘルプのURLはblogspot.comになっていますが、
日本向け(?)はblogspot.jpとなりますので、気をつけて下さい)

それから、サイト側でのフィードの許可は
管理画面の「設定」→「その他」→サイトフィードの所で設定します。
(多分、何もしなければ、「完全」で設定されていると思われる)

ただ、Google Readerを利用する場合はブラウザのURLをそのまま
コピペで登録すれば購読できるようになったりしますね。
(RSSリーダは、外部サイトとGoogleのサービスが利用できるのであれば、
Google Readerをおすすめします。
購読するRSSを仕事用とPrivate用で分けたい場合などは話は違ってきますが、
(あくまでも、個人的な感覚では)複数の設定を持っててもあまり便利ではないかと思いますので。。)

BloggerのフィードURL
http://support.google.com/blogger/bin/answer.py?hl=ja&answer=97933


2012年4月5日木曜日

BloggerとGoogle+を連動させる。

例えば、Blogを投稿した後、告知とかの目的で
Google+に投稿することがあると思います。

Blogの公開と同時にGoogle+への投稿が自動的にできるようになりました。

方法は、以下の設定をすることで可能になります。

  1. Bloggerのマイブログページの右上にある歯車をクリック
  2. Google+に接続するを選択
  3. BloggerのプロフィールにGoogle+のプロフィールを使うよ?と言われるのでOKする。
  4. 再度ログイン(自動的にさせられる)
  5. Bloggerのマイブログページに移動し、設定  ›  投稿とコメントメニューを選択
  6. Google+に共有の設定が増えているので、「はい」に設定する。
Bloggerヘルプ(投稿をGoogle+で共有する)
http://support.google.com/blogger/bin/answer.py?hl=ja&answer=1752748&ctx=go

そうすると、公開ボタンを押した後、共有する入力欄が出てくるはず!

2012年3月29日木曜日

Google App Engine Go SDK 1.6.4を使おうと思ったものの…。

今日の早朝(1:00頃)Go Ver 1のリリースと、
Google App Engine Go SDK 1.6.4がリリースされたので、
変更点をまとめようかと思ったのですが、go1beta4を利用していると
あまり大きな変更点はないので、変更点を洗い出すよりは、開発の手順を
まとめてみようと思います。

とりあえず、ネタを考えるのが面倒だったので
Slim3のチュートリアルと同じ事をGAE/G 1.6.4で実現できるかどうかを
試しつつ進めていこうと思います。

  1. コントローラとテストの作成
事前に、プロジェクトのディレクトリを作成して下さい。
$ cd [任意のディレクトリ]
$ mkdir twitter
$ cd twitter

Slim3のように、build.xmlなんて、便利なものは存在しませんので、手で作成します。

app.yaml
application: twitter
version: 1
runtime: go
api_version: go1

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

- url: /.*
  script: _go_app

app.yamlの大きな変更点は、api_versionが"go1"になっている点です。
1.6.3までは、"3"で、go1betaは"go1beta"でした。
恐らく、古いバージョンはコンパイルエラーが発生して動かないと思いますので
古いバージョンのプログラムから持ってくる場合はチェックしましょう。

次に、controllerを作ります。
$ mkdir controller
$ cd controller

frontcontroller.goを作成します。このファイルが名前の通り、frontcontrollerとなります。

package controller

import (
 "fmt"
 "net/http"
)

func init() {
 http.HandleFunc("/twitter/", index)
}

func index(w http.ResponseWriter, r *http.Request) {
 htmlText := `
     <!DOCTYPE html>
     <html>
     <head>
     <title>Hello World</title>
     </head>
     <body>
         <p>Hello World</p>
     </body>
     </html>
     `
  
 fmt.Fprintf(w, "%s", htmlText)
}

では、実行します。
開発サーバはdev_appserver.pyを実行するだけなので、twitterディレクトリの1つ上の
ディレクトリで実行すると便利です。

$ python [SDKのディレクトリ]/dev_appserver.py --high_replication twitter

ブラウザで、http://localhost:8080/twitter/にアクセスして、Hello Worldが
表示されれば成功です。
--high_replicationのオプションはHRD環境をエミュレートするオプションです。
現時点でこのオプションを付ける必要は無いのですが、とりあえず入れておきます。

この後、デモでは、フォームの作成で、モデルの処理を作成して、テストコードを
作成してテストを動かしてテストして…と続くのですが、

Go言語標準のコマンド、go testが動作しないため、断念しました。
https://plus.google.com/u/0/114183076079015753160/posts/intFioopvTj

ドキュメントにも載っていないし、恐らく、現時点ではGAE/GのSDKでgo testを利用する事はできないのかもしれません。

ただ、GAE/Gにもtest用のモジュールが存在しているようで、
https://code.google.com/p/gae-go-testing/source/browse/appenginetesting/
これを利用したら良いのではないかと考えている最中です。

17:16:追記
gae-go-testingはだいぶ古そうです。試しにmake installしてみましたが、だめでした。
unit testについてはもう少し様子を見たほうが良さそうです。

もしくは、標準のGo言語のコンパイラをインストールしてGAEのAPIを動かさない
パッケージのみテストを行うという方法を取るしか今は無さそうです。

2012年3月27日火曜日

Google API Expertが解説するClosure-libraryプログラミングガイドを読む(2)



今日も上記のClosure-library本を読み進めているのですが、
少し、DOM操作のコードを動かしてみたので、
DOM操作についてのメモを残しておきたいと思います。

まず、base.jsとdeps.jsを取り込むコードを入れておきます。
  <link rel="stylesheet" href="closure-library/closure/goog/css/common.css" />
  <link rel="stylesheet" href="closure-library/closure/goog/css/hsvpalette.css" />
  <script src="closure-library/closure/goog/base.js"></script>
  <script src="deps.js"></script>

で、最初にDOMのモジュールを読み込みます。
<script>
    // DOMの追加モジュールの読み込み
    goog.require('goog.dom');
    goog.require('goog.dom.DomHelper');
</script>

次に実際にDOMの追加です。
<script>
    // iframeをbodyに追加する。
    var iframe = goog.dom.createDom('IFRAME', {'src': 'about:blank'});
    goog.dom.appendChild(document.body, iframe);

    var fdoc = goog.dom.getFrameContentDocument(iframe);
    var helper = goog.dom.getDomHelper(ifdoc);

    // iframeの中にdivとspanを追加
    var node = helper.createDom(
          'DIV', null,
          helper.createDom('SPAN', { 'style': 'color: red;' }, 'Hello '),
          helper.createDom('SPAN', { 'style': 'color: blue;'}, 'World!'));
    helper.appendChild(ifdoc.body, node);
</script>

ソースコードを見ていただくと分かりますが、 bodyにiframeを追加して、iframeの中にHello World!が追加されます。 createDom()が要素の生成で、appendChild()が要素の追加。 getDomHelper()でhelperを取得して、helperを通じて、 要素の子要素を追加するという感じになっています。

CSSセレクタを使った要素の取得もできるようなのですが、
どうやら、closure-libraryと違うライセンスが適用されるようで、
商用利用の場合は、気をつけたほうが良いようです。

使い方は、こんな感じだそうです。

var hoge = goog.dom.query('#list > li:nth-child(hoge)');


オープンソースカンファレンス2012@愛媛に参加しました。

オープンソースカンファレンス2012 Ehimeに参加してきました。

今回は、中国GTUGの開催ではなく、四国GTUGが出展するので、
セッションを担当してきました。

当日の資料

今回は四国GTUGのブースに行ってもらう事を促す内容を
多くしてみました。実際、効果があったのかどうかはわかりませんが、
ある程度、効果があったと信じています。

次に、Go言語でプログラミングをする最初の一歩として、
開発環境、情報源などを紹介。

最後に、Go言語のライブコーディングをしながら、
書き方の説明を2例(本当は4例ぐらい考えていましたが、
前半で時間を使い過ぎて足りませんでした)

おまけにプレゼント(謹呈本)を「会場から一番近い人」に渡しました。

一番近い人にした理由は、大学生か、地元の人の可能性が高いと
踏んだからです。(後でまとめますが)今回のセッションの参加者は
県外の方が多かったように見受けられたので、せっかく愛媛で開催
しているのに県外の方に渡すのはおもしろくなかったので。

セッションの参加者は25人程度だったように思います。
事前申し込みは20人。同一時間帯のセッションの中では1位でした。
(運営側がほとんど、裏番組の宣伝を行ってくれるので、1週間前は4人とか
でしたが、何とか盛り返したということでしょうか。)

そして、Twitterなどでも、私のセッションが「おもしろかった」と
言っていただいた方がいらっしゃるようで、任務は全うできたかなと思っています。
Go言語の利用者の人口が増えるかどうかは今後の様子を見守ろうと思います。

岡山から愛媛まで、3時間半程度かかったようなので、やっぱり当日移動だと、
他の人のセッションが聞けないので残念でした。
(懇親会の為に途中、一旦ホテルにチェックインしに行っていて、
自分のセッション+ブースでの待機ぐらいしかできなかった)

懇親会にも参加させていただいたのですが、
久しぶりにすみともさん、ありやまさんとお話して、「関西には来ないのか?」
と言われたので、ある程度、余裕ができたら、
何らかのイベントに顔を出しに行きたいと思います。

最後に、のがたさんとお会いしたので、昨年から開発していた、「Sahanadroid」の
開発終了の相談をさせていただきました。
急なオファーから始まった開発でしたが、途中でサーバとの連携テストを残して、
中断状態が続いていたのですが、決着をつけないといけなかったので
私から切り出すことにしました。
ソースコードはすべて公開してありますので、参考になるかどうかわかりませんが、
興味がありましたら見ていただけたらと思います。

Sahanadroidのソースコード(Android 2.2で開発)
https://github.com/tyokoyama/SahanaClient
テストプロジェクト
https://github.com/tyokoyama/SahanaClientTest

このプロジェクトで、貴重な経験ができましたので、今後も何らかの形で
協力できればと思っています。