2012年1月29日日曜日

JQueryの初歩

最近、「コアjQuery+プラグイン/jQuery UI開発技術実践技法」という本を
読んでいるのですが、

この本です。


jQueryの基本的な部分をまとめておこうと思います。

使い方としては、jQueryのソースコードをダウンロードしてきて、
サーバのディレクトリに置いておき、それをロードして使えば良いのですが、
Googleが提供しているJavaScriptAPIを利用すれば、指定したバージョンの
jQueryのソースコードをGoogleからダウンロードして使う事ができます。

例えば、以下のようになります。

<script src="http://www.google.com/jsapi">
</script>
<script>
 google.load("jquery", "1.7");
 google.load("jqueryui", "1.8");
</script>
<script>
 $(function() {
  $("#datepicker").datepicker({
   dateFormat: 'yy/mm/dd'
  });
 });
</script>

これは、HTMLのidにdatepickerと指定されている要素を取得し、
そこに日付入力ダイアログを表示し、入力後、要素に反映するというコードです。

headタグの所に、上記のコードを入れておくと、ブラウザにページが表示された時には
日付入力ダイアログが表示されるようになっています。

まずは、jQueryを使う上でよくわからないのが、$()です。
これは、「要素の配列を返す関数」だそうです。引数に特定の文字列を指定すると、
ページの中に配置されている要素から条件に一致する要素の配列を返してきます。

例えば、
$("div.header")
と、指定すると、ページの中にあるdiv要素でclass="header"と指定されている要素が
取り出されます。したがって、
$("div.header").hide()
とすると、divタグのclass="header"の要素が非表示になります。

次に、
$("div#header")
と指定すると、ページの中にあるdiv要素でid="header"と指定されている要素が
取り出されます。したがって、
$("div#header").hide()
とすると、divタグのid="header"と指定されている要素が非表示になります。

最後に、
$("div a")
と指定すると、divタグの中に含まれるaタグの要素が取得されます。
$("div a").style.backgroundColor="#FF0000"
とすると、divタグの中に含まれるリンクの背景が赤くなります。

という事で、まとめると、

  • $()関数の中に指定する文字列はページ内の要素を取得する条件を指定する。
  • $()関数は条件に一致する全ての要素を取得する。
  • "."で区切ると、classの条件、"#"で区切るとidの条件、" "(空白)で区切ると要素の中に含まれる要素が取得される。
  • "."、"#"、" "は混在(複数の条件を指定)して使うことができる。基本AND条件となる。


これを利用すると、マウスの動きに連動して背景を変えるなんてことも簡単にできるようになります。

他にも、$()関数の引数に関数を渡すと、「ドキュメント・レディ」ハンドラと
言って、JavaScriptでいうと、window.onloadで実行する処理に相当するものだそうです。
したがって、headタグの中のscriptタグ内で、
$(function () {
    $("input#name").hide();
});

なんて処理を書いておくと、inputタグのid="name"の要素を初期状態を非表示にできます。

便利なので、しっかり使ってみたいと思います。

2012年1月25日水曜日

Slim3 Source Code Reading #5

今日は、Slim3 Source Code Reading #5の日でした。

今回も@sinmetalさんと2人での開催となりました。
内容は、

  1. 私の仕事でGoogle App Engineを使ったアプリケーションを作ることになったのでその質問
  2. Memcache.javaを読み進める。
  3. UserServiceを使った認証についての雑談
  4. org.slim3.datastoreパッケージのCreationXX.javaについて

でした。

1.は単純に仕事で使う事になった(した)ので、アプリケーションを開発している話を少ししました。

2.が本題のMemcacheの機能です。
方向性として、Memcacheの存在確認、MemcacheへのGet、Putを中心に読みました。
(今、deleteも確認しましたが、deleteもラッパーでした)
読み進めた所、Memcacheの存在確認はcontains(key)、Memcacheへの登録はput()を
それぞれ呼び出しますが、中身は、MemcacheServiceのメソッドを呼び出している
だけでした。

しかし、Getだけはそのままではなく、MemcacheDelegate.javaの214行目から
始まる、getInternal()でいろいろ処理されています。
MemcacheServicePb.MemcacheGetRequestクラスのインスタンスと、
MemcacheServicePb.MemcacheResponseクラスのインスタンスを生成し、
233行目のmakeSyncCall()を呼び出します。

このmakeSyncCall()は中で、ApiProxy.makeSyncCall()を呼び出し、
(恐らくSDKよりも下の)APIを呼び出し、Memcacheに登録しています。
MemcacheはKey-Value形式で、しかも、Valueはbyteデータを格納する事に
なっていますので変換処理が必要ですが、オブジェクトをbyteデータに変換する処理も
このあたりで行われているようです。

なぜ、このようにGetだけ、ただのラッパーではなかったのかは、分かりませんでした。
(恐らく、AppEngineのAPIを実行すると処理が遅いとか、無駄な待ち時間がかかるなど
あるのだろうと判断して終わらせました)

その次は、Memcacheの次に読み進めるのをどれにするか選んでいた時に、
ユーザ認証の話になり、私が、UserServiceのすばらしさを一方的に話した感じでした。

利点として、

  1. Googleの認証機構に全てお任せできる。
  2. 認証済みであれば、ユーザ情報が取れるので、利用できる。(Mail APIによる通知や、ユーザの識別、データの管理、アクセス管理など)
  3. データストアに(多分)SessionIDなどを登録する必要がなくなる。


1つ目は自力で認証処理を作る手間が省ける。かつ、(多分)自分よりも質の良いセキュリティが確保できる。しかも、Gmailが見れる状態(すでにログイン済み)だと、
アプリケーションの認証の処理が不要な場合もでてくる(入力の手間が減る)。
2つ目は自力認証の必要が無いので、ユーザ情報の有無で表示の制御ができる。何らかの処理の完了通知などにも利用できる。限られたユーザのみ見せるという機能も簡単に作れる。しかも、認証情報をデータストアに残す必要もないので、データストアのオペレーションの削減に繋がる。
3つ目はSlim3だとSession情報をデータストアに格納している(?)ということなので、
cronなどで、データストアの不要な情報を削除する処理を実行しなければならないが
その手間が省ける。データストアのオペレーションにも影響する。

ということが考えられます。

最後に4つ目のCreationXX.javaについてですが、org.slim3.datastoreパッケージに
CreationUser.java
CreationDate.java
CreationEmail.java
ModificationUser.java
ModificationDate.java
ModificationEmail.java

というファイルが存在しています。
このファイルはAttributeListenerインターフェイスを実装しているのですが、
何のためにあるのかよくわからないファイルでした。

が、実は(ドキュメントにも書かれているのですが)、アノテーションを設定すると、
ModelMetaで、データストアにPutする前に、現在の日付や、ユーザ情報を自動的に
取ってきて、プロパティに設定してくれます。
(Serviceで設定する必要がありません)

    @Attribute(listener = CreationUser.class)
    private User user;

上記のようにすることで、勝手に登録されます。
CreationXXとModificationXXの違いは、
CreationXXはすでに設定済みかどうかを判定し、設定されていない場合のみ登録されます。
ModificationXXの場合は、内容にかかわらず、値を上書きします。

次回は1/31(火) 19:00〜で、util系を読み進める予定です。

2012年1月22日日曜日

本格アプリを作ろう!Androidプログラミングレシピを読みました。



本格アプリを作ろう!Androidプログラミングレシピを読みました。
この本はインプレスジャパンから発行されている書籍です。

内容は、Android2.3(Gingerbread)で動作するソースコードを元に
記述されています。翻訳担当の方が動作確認とAndroid4.0での動作についても
注釈として記述されているため、最新のAndroidに対するサポートもされています。

概要は以下の内容です。

  1. Androidを始めよう
  2. ユーザインターフェイスのレシピ
  3. 通信ネットワークのレシピ
  4. 端末ハードウェアやメディアと連携するレシピ
  5. データ永続化のレシピ
  6. Androidシステムと連携するレシピ
  7. ライブラリを活用するレシピ
  8. 付録
    1. スクリプト環境の構築
    2. Android NDKで性能を高める
    3. アプリ設計のガイドライン
本書は、基本的な所から、大体のデバイスの使い方、アプリケーションの連携が
説明されています。
文章量は多く感じられますが、詳細に説明されているため、じっくり読んでみる方が
良いと思います。また、レシピ形式で細かく分けられているため、ゆっくり進める事も
可能です。ソースコードもほぼ掲載されているので、写経も可能です。

1点気になったのはリソースの解放処理が省略されている事です。
Android2.3だと不要なのか、本来の機能の説明のために省略したのか
分かりませんが、画像とか、ネットワークリソースの解放の記述が無かったのが
気になりました。

この書籍は今回は献本して頂きましたが、この書籍は購入しても損はしないと
思いました。

Google+に投稿したコメント
https://plus.google.com/u/0/114183076079015753160/posts/ULQoezrSWN4
https://plus.google.com/u/0/114183076079015753160/posts/UPiADfBJ1TG

オープンセミナー2012@広島で発表してきました。

オープンセミナー2012@広島で講師として発表してきました。

今回はQueryとTransactionについてお話ししました。
資料とソースコードは公開してありますので、興味がありましたらごらんください。

資料
https://docs.google.com/present/edit?id=0ASEzu8hKOt7rZHJwbjl2NV83MWRreDV6dmMy

デモ用ソースコード
https://github.com/tyokoyama/osh2012demo

今回はQueryとTransactionなのでそんなに話題として
少ないかな?と思ったのですが、
よく考えると、Queryを語るにはIndexの前提も必要で、
Transactionの話をしようとすると、エンティティグループの前提も
必要だったので、当日追加してみたのですが、早口でお話ししないと
いけないぐらいの内容になってしまいました。
今思えばTransactionは外せば良かったかもしれません。
しかも、Go言語と言いながらほとんどGoogle App Engineの仕組みの話になってしまったような。

Queryについても文字列のInequalityFilterに関しての動作も
今回のデモ用のアプリケーションだと良い例ではなかったのかもしれません。
住所録の方が良かったかも。前方一致の検索結果になるのを見せるには
そちらの方が説明しやすい結果もでたかもしれないし。

他の講師の方の話も聞きたかったのですが、資料の調整と
すぐに反省モードに入ってしまったので、あまり聞けていません。

その後、懇親会で、宇品港(広島港)の前の公園の中にある、テントでかきを食べる
かき小屋で懇親会をしました。食べ物は精算所で先に買って、(追加する時も精算所に行く)飲み物は自販機で買うスタイルで、テント小屋の中で炭焼きで海鮮、肉類を焼いて食べました。

その後、@Toro_kunの家に行って、たこ焼きを初めて焼きました。そして、食べました。
深夜まで色々な真面目な話をして、満足して帰りました。
ありがとうございました。

2時から@soudai1025さんを車で送り届けて、朝5時に無事家に付きました。

次は第12回中国GTUG勉強会に出席の予定です。

2012年1月18日水曜日

Slim3 Source Code Reading #4

今日はSlim3 Source Code Reading #4を開催しました。
参加者が私と@sinmetalさんの2人だったので、当初想定していたメンバーになりました。

今回は、

  1. EntityGroupとQueryについての前提知識の共有(というよりは、前回間違った説明があったのでその辺りの共有)
  2. Transaction、GlobalTransactionのソースコードを読み進めました。
  3. EntityGroupに関する実装上の話題。構成とか。Ancestorクエリにするケースなど。
  4. sinmetalさんの「サービス単体でテストコードを動かすと、Cross Group Transactionが正常に動作するのに、プロジェクト全体のテストを行うとテストに失敗する件」

を行いました。

Transactionについては、Datastore.javaにあるDatastore#beginTransaction()を読み進めました。
ただ、この中身は、Google App EngineのTransactionAPIを利用しているだけで、Slim3で特別何かをしているということはありませんでした。
ただし、TransactionのOptionでSystem.getProperty("slim3.useXGTX")の結果がTrueである場合、Cross Group Transactionが有効になります。
System.getProperty("slim3.useXGTX")はappengine-web.xmlに記述してある値を取得します。※現時点ではデフォルトでは記述がないので、値はfalseです。

GlobalTransactionに関しては、かなり大変な事をしてあるので、概要だけ説明を書いておきます。
(sinmetalさんが検証コードを書いて試してくれるという事なので、その結果を待ちます)
実際、GlobalTransactionが必要になる事はこれからは少なくなってくると思いますので
(現時点で既にDeprecated)Cross Group Transactionを利用した方が良いと思います。

具体的にはどうしているかと言うと、

  1. トランザクションを開始する。Datastore.beginGlobalTransaction();
  2. エンティティをGet or Putする時、Lock用のキーMapに、現在のキーの祖先が存在しているかを確認する。存在していれば、例外をスローする。ただし、存在していても、キーのTimestamp+30秒を超えていれば無視する。
  3. GlobalTransaction#Get or Putを呼び出す。その時、対象のエンティティの祖先のキーを取得し、Lock用のMapに退避しておく。キーにはTimestampを保存しておく。また、Getの場合はKeyのみ、Putの場合はKeyとEntityのペアをJournal用のMapにPutする。
  4. トランザクションをCommit or Rollback(GlobalTransaction#commit、rollback)する。この時、GlobalTransactionの場合は、Journal用のMapからGet用のKeyを削除し、Put用のEntityをslim3.GlobalTransactionカインドにPutする。Get用のKeyにはEntityが登録されていないので、Entity==nullであれば、Get用のKeyと判定する。ただし、単一EntityGroupのみの場合(Lock用のMapが空)はLocalTransactionと判断しそのままCommitする。
  5. GlobalTransactionの場合、TaskQueueにキューを登録する。登録するキューはGlobalTransactionServletへのリクエストとして登録する。
  6. GlobalTransactionServletで、RollForwardトランザクションの場合、Commitされた場合は、GlobalTransaction用のカインドにある、KeyとEntityのペアを取り出し、本来のカインドのエンティティにPutする。Rollbackトランザクションの場合は、カインドから対象のエンティティを削除する。

大体の処理は上記のようになっています。
正確な処理については、大変ですが、ソースコードを読んでみてください。

最後にsinmetalさんから相談された、「単体テストでHRDのテストが動作しない件」ですが、見せていただいたテストコードには、setUp()でSystem.setProperty("slim3.useXGTX", "true");の
記述がありましたが、この設定を反映するAsyncDatastoreDelegateが何度もインスタンスが生成されるようであれば良いのですが、メンバとして保持している、
Datastoreクラスがstaticであるため、実行開始後、1回しか設定を読み込まないようになっているようです。したがって、System.setProperty()が呼び出される前にDatastoreクラスのメソッドが呼び出されてしまうとその時点でデフォルトのトランザクションオプションが反映されてしまって、System.setProperty()が意味をなさなくなってしまいます。

これについても検証コードの結果を待ちましょう。

ただ、この後にも話をしたのですが、開発環境HRDのオプションの遅延は、
かなり大きいので、Queryのテストをしたい時にまでHRDの遅延が発生すると
テストが通らない問題に直面してしまいます。
一つは、遅延確率を設定するオプションを0にして遅延しなくするという方法を
提案しましたが、テストクラスの本数が多くなってくると面倒という問題があるようです。
また、Thread.sleep(2000)とか入れてみるという提案もしたのですが、
これも同様に本数が増えてくると、テストの実行時間が長くなってしまうので
全体のテストを行う時に問題になる。ということでした。

今日は、時間も長めにやりましたが、内容も濃かったです。
疲れました。

2012年1月14日土曜日

Go言語プログラミング入門の正誤表を更新しました。

Go言語プログラミング入門正誤表に追記しました。
http://takashi-yokoyama.blogspot.com/2011/12/go.html

appengine/datastoreパッケージのPropertyLoadSaverについて、
誤記があったので、詳細に説明をしておきたいと思います。

PropertyLoadSaverインターフェイスのドキュメントです。
http://code.google.com/intl/ja/appengine/docs/go/datastore/reference.html

書籍では、「データを登録した状態でないとプログラムが応答を返さなくなる」という
事が書かれているのですが、これは、サンプルコードを作成している時の試行錯誤の
途中段階の誤認識をした状態で記述をしていました。

実は、PropertyLoadSaverインターフェイスのLoadメソッド、Saveメソッドの定義は
以下の通りです。(書籍のサンプルコードから抜粋したものです)
func (g *Guest) Load(c <-chan datastore.Property) os.Error {
func (g *Guest) Save(c chan<- datastore.Property) os.Error {
どちらもchannelが引数になっています。このchannelについての説明はP.63付近に
少し記述をしていますが、CriticalSectionと同じで、自分のスレッドをブロックします。
ブロックが解除されるのはchannelに値がセットされた時か、close(c)された時と
なります。LoadStruct()、SaveStruct()はメソッド内でchannelの処理を行っているため、呼び出せば構造体に値がセットされ、channelの処理も適切に行ってくれます。

書籍のプログラムだと、Saveメソッドの方では、何もしていないため、channelのブロックが解除されないことになり、現時点ではプログラムが応答を返さなくなってしまいます。

したがって、Load()の方でも、同様に「何もすることがない」からといって何もしないと
プログラムが応答を返さなくなってしまいます。

2012年1月12日木曜日

AppEngine Office Hourに参加した。

Japanese AppEngine Office Hourに参加したので、そこでの質問と回答をメモ
https://plus.google.com/u/0/110554344789668969711/posts/d3ykURrCnDt

Python2.5以下でないとssl警告が出る件
→Goのappcfg.pyはpython2.5が指定されているので、
$ python ../appcfg.py update app
など、python新しいバージョンのPythonを明示的に指定すると、警告はでない。
または、python2.5の方にsslモジュールを適用する。

ユーザ名を取得する場合。
初回ログイン時に表示名を入力するなどしてもらって、表示名を入手する。
ATNDとかどうだったかな…?
→多分、同じ動作をさせていると思われる。
→確認したらユーザ設定画面があって、そこでユーザ名を登録していた。

Emailの先頭部分(@以前)を表示
→だと、メールアドレスがバレてしまうので…。
上記の対応を行うしか無いだろう。

Java、PythonもNicknameを取得すると、メールアドレスの先頭部分が取れるかも。

Goを使うメリット
・ネイティブであるので、立ち上がりが一番速い。メモリの効率も良い。

XGTransactionの最大エンティティグループについて(仕様で最大5グループまで)
http://code.google.com/intl/en/appengine/docs/go/datastore/overview.html#Cross_Group_Transactions

開発環境でHRDのオプションを入れるコマンド
$ python ../dev_appserver.py --high_replication ./app


2012年1月11日水曜日

Support Packageを使ってFragmentを利用する

先日、私が公開している「サポセン前の問診票」アプリケーションにFragmentを適用した
形に作り替えたので、その時に発生した現象と、Framgentの使い方をメモしておきます。

前提:
※Support Package v4を利用する。

以下が大体の手順です。

  1. レイアウトXMLにFragmentを含むレイアウトを定義する。
  2. FragmentActivityを継承したActivityのクラスを作成する。
  3. 普通に起動。


だけです。終わり。というと内容が薄いので、

レイアウトは以下のような感じになります。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <FrameLayout
        android:id="@+id/framelayoutmain"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >
     <fragment
         android:id="@+id/fragment1"
         android:name="com.sample.ui.MainFragment"
         android:layout_width="fill_parent" android:layout_height="fill_parent" />

    </FrameLayout>
</LinearLayout>

Activityのソースコードはこれまで通りとなります。

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Activityが実行された時点でFragmentも呼び出されて表示されますので、
Fragment外で処理する事が無ければ、Activity側では何も記述しなくても良いです。
ポイントとしてはFragmentActivityを継承しないと実行時にエラーになってしまいます。

あとは、Fragmentのプログラムの方で処理するプログラムを
実装すれば良いです。

次に、FragmentをBackStackに積む方法は以下のコードです。

SubFragment fragment = new SubFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.framelayoutmain, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack(null);
ft.commit();

FragmentTransactionクラスがFragmentのBackStackの状態を管理しているようです。
ft.replaceでレイアウトに配置されているFragmentを置き換えます。
これは、Fragmentが複数配置されている場合でも、それぞれのFragmentが配置されているViewを指定して置き換える事ができます。
setTransitionは置き換え時のアニメーションを指定します。
addToBackStackは置き換え後の状態(アクティビティ全体)をBackStackに積みます。
最後にcommitを実行して確定します。

注意点として、ft.replaceした場合、下にあるFragmentの内容が見えてしまいます。
(背景が透明になる)
したがって、背景を画像や色を指定して、下のFragmentが見えないようにしないと
いけません。(これはSupport Packageのみの現象?)
しかも、ListFragmentを利用した時(他のFragmentでも?)、画面をタッチすると、下に配置されたFragmentもタッチを検出しますので、処理が実行されてしまいます。
BackStackに積む前に下のFragmentの処理が実行されないようにEnabled = falseにするか、
getFragmentManager().getBackStackEntryCount()をチェックして、積まれていたら
処理をスキップするなど工夫をしないといけないようです。

Google+に書き残したコメントです。

https://plus.google.com/u/0/114183076079015753160/posts/cyodhHmgRy9
https://plus.google.com/u/0/114183076079015753160/posts/3kDyzNe1mej
https://plus.google.com/u/0/114183076079015753160/posts/UHcTUgp7Hky

Slim3 Source Code Reading #3

Slim3ソースコード読書会(3)を開催しました。

今回は、前回のPutとかぶっていますが、以下のソースコードを読みました。
Datastore.java
DatastoreDelegate.java
AsyncDatastoreDelegate.java
ModelQuery.java
AbstractQuery.java
DatastoreUtil.java

そこで話題になったポイントをメモしておきます。

1点目。
AsyncDatastoreDelegate.javaの642行目で、

mm.validateKey(key);を呼び出し、KeyのKindとModelMetaで保持しているKindを
比較し、一致しなければIllegalArgumentExceptionをスローしている件。

これは、以下のように、取得しようとしているModelとKeyが一致しない時などに、
エラーにするための防波堤ではないか。という結論になりました。

例えば、以下のようにTweetクラスとTweet2クラスが存在したとして、
プロパティの構成が全く同じ場合で、データストアにKeyが存在している場合。

Tweet tweet = new Tweet();
Datastore.put(tweet);
Tweet2 tweet2 = Datastore.get(Tweet2.class, tweet.getKey());    // 指定したKeyとModelが不一致

データストアからのGetは成功しますから、構成が同じであれば、Tweet2クラスの
Modelとして値が取得されます。しかし、これは本来Tweetのデータであるため、
Modelが取れてはいけないのですが、データストアの仕組み上防ぐことができません。
したがって、ModelMetaで定義しているKindとKeyのKindを比較して一致しているか
どうかを確認していると思われます。

2点目。
ModelQuery.javaの241行目のasList()で最後にfilterInMemory()とsortInMemory()を
呼び出していることについて。

最初は、「DatastoreのQueryでsortやFilterを指定したらメモリ上でのソートやFilterは不要だろう」と
いう意見だったのですが、よく読んでみると、呼び出し元でfilterInMemory()を
呼び出していなければ、inMemoryCriteriaのSizeが0なので、実際には
Filter処理は実行されません。同様に、sortに関してもinMemorySortCriteriaの
Sizeが0であればSort処理も実行されません。

ただし、データストアでSortを実行したあと、sortInMemoryを呼び出していれば、
両方とも実行されることになります。

恐らく、inMemoryFilterについてはOR条件で抽出したい場合があれば、
それを実現するために利用するのではないかという結論になりました。
(AND条件での検索であれば、QueryにFilterを設定するだけで良いが、データストアでOR条件は指定できない。)

次回は1/17(火)19:00〜の予定です。

2012年1月10日火曜日

Slim3 Source Code Reading #3の前に(その2)

午前中にGetについての記事を書いたのですが、
Queryについてもメモを残しておきたいと思います。

実はQueryについては読む予定は無かったのですが、
開発環境での問題なのか、Putした結果が反映されるのが遅いという現象が
発生したので、Datastore.query(t).asList();という書き方がおかしいのか?と
思って、読み進めてみました。
実際はGetと同じくデータストアの読み取りなのでこちらも読んでおくべきと
思ったのでメモしておく事にします。

先に概要をメモ

  • Datastore.query(class)を呼び出すとDatastore.java3965行目が呼び出される。
    • 中ではDatastoreDelegate.query(class)を呼び出すので、DatastoreDelegate.java2397行目が実行される。
      • この中身はAsyncDatastoreDelegate.query(class)を呼び出すだけ。
        • AsyncDatastoreDelegate.java1940行目が実行される。
          • 中身はModelQueryクラスのインスタンスを生成してreturn
            • コンストラクタの引数は、AsyncDatastoreServiceとModelMetaクラス
            • ModelQueryクラスのコンストラクタでappengineAPIのQueryクラスを生成する。
まとめると、「Datastore.query()はModelMetaで指定したKind名のQuery」を生成する。
という簡単なものでした。

では、asList()メソッドを呼び出すとどうなるか。
ModelQuery.java241行目が呼び出されます。

最初にappengineAPIのPreparedQuery#asList(options)が呼び出され、結果が返ります。(トランザクションがセットされていればトランザクションも使われます)

そして、PreparedQuery#asList()で取得された結果(Entity)をModelMetaに変換し、
ModelMetaからModelクラスへの変換を行います。(取得された件数分全て)
ここで、Model#postGet()も呼び出されますので、このタイミングで編集処理が
実行されます。
よって、データ数が多いときには、適切にLimit指定を行わないと、
「表示しない(使わない)データを処理する」ことになるケースもあるということになります。

Slim3 Source Code Reading #3の前に

本日、Slim3 Source Code Reading #3を開催します。

恐らく、今日はGetとTransactionを読む予定になると思いますが、
Queryについても読み進めておく必要がありそうです。

まず、Getから。
GetはDatastore.java835行目の
public static <M> M get(Class<M> modelClass, Key key)
から読み進める事になると思います。

getメソッドの中では、メンバに持っているDatastoreDelegate#getが呼び出されます。(838行目)
これは、putの時と同じです。

DatastoreDelegate#getはいくつかメソッドがありますが、
748行目→769行目→771行目でカレントのトランザクションを取得し、
カレントのトランザクションを利用して1099行目を呼び出します。

1099行目のgetも、中身は非同期のgetAsync()を呼び出すだけです。
return FutureUtil.getQuietly(async.getAsync(tx, modelMeta, key));

FutureUtil.getQuietly()はFuture#get()を呼び出し、非同期の結果を受け取る+例外をキャッチするのみですので、結局「非同期で処理されるのを待つ」という感じでしょうか。
(これは2回目で読んだPutと同じです)

async変数はAsyncDatastoreDelegateクラスなのでそちらに進みます。

AsyncDatastoreDelegate#getAsync()はAsyncDatastoreDelegate.javaの630行目に
あります。
この中では、560行目のgetAsync()をwrapしているだけです。
560行目のgetAsync()は、中で1439行目のgetAsMapAsync()を呼び出します。
ここでは、DatastoreUtil#getAsMapAsync()を呼び出しているだけです。
DatastoreUtil#getAsMapAsync()はAsyncDatastoreService.getを呼び出し、データストアから実際に読み取ります。

ここまでの流れです。

  • Datastore#get()からDatastoreDelegate#get()を呼び出す
    • 748→769→771で、カレントのトランザクションを取得し、1099行目
      • FutureUtil.getQuietly(async.getAsync())
        • AsyncDatastoreDelegate.javaの630→560
          • 1439行目のgetAsMapAsync()を呼び出す。
            • DatastoreUtil.getAsMapAsync()を呼び出す。
              • AsyncDatastoreService#get(tx, keys)
まとめると、カレントのトランザクションで非同期APIを呼び、データストアからエンティティを呼び出す。

エンティティ→modelへの変換はAsyncDatastoreDelegateの640行目で
行われています。
この時点で、データストアから読みだしたタイミングで値を編集するpostGet()も呼び出されます。

明日以降の授業の準備もあるので、今日はここまで。

2012年1月3日火曜日

2012年の目標

明日(というか、今日)で正月3日間が終わるので、
そろそろ目標を決めていきたいと思います。

※まとめていたら長文になってしまいました。

毎年、目標を設定しても暑くなる頃には頭から消えてしまうわけですが、
昨年(2011年)の実績ではいい感じだったので、今年も書いてみることにします。

まず、中国GTUGの活動について。

確定事項として、

  1. オープンセミナー@広島での発表(1/21(土))
  2. 岡山在住の技術者の方に中国GTUGスタッフのお願いをする。
  3. 第12回勉強会@岡山
1つ目のオープンセミナー@広島に関しては以下のGoogle+のコメントを参照して下さい。
https://plus.google.com/u/0/114183076079015753160/posts/eSTnHeY7cfm

2つ目のスタッフについては、私の仕事関係がうまくいかなくなる(仕事がなくなる)
可能性が非常に高いので、岡山で私以外の方にも開催に関して
手伝っていただかないといけなくなる事を想定しました。
これまで私一人で準備していた部分をお願いしようと思っています。
(広島には2人の方にお願いしています。場所の確保が主な内容のつもりです)

参考までに。
スタッフとしてお願いする条件を個人的に決めていました。

  1. 他のコミュニティのスタッフを兼任していない方
  2. 1に該当しない方で、ある程度制御が効きそうな方。
  3. 中国GTUGの運営ルールに則って活動していただけそうな方
2と3は現時点では私の運営の考え方に反しない方になってしまっているのですが
1が最も重要なポイントだと思います。
スタッフの方には、ある程度イベントの開催の許可をしようと思っているのですが
兼任していると、例えば、「共催」という形でGoogle技術を扱わないような
イベントを開催されたりする可能性を考えざるを得ません。

まだ、結成して1年半で、スタッフも私を含めてやっと3人という状態なので、
そこまで運営ルールというものを厳密にやりとりもしていません。
ということから、私が信頼できる方を厳選しているというのもあります。
(広島のスタッフの方は上記の2に該当する方ですが、怪しい事はしないと思ったので
お願いしました。それでも結成から1年経ってから(?)だったように思います)

これからスタッフのお願いをしようと思っている、岡山の方は、皆さん長い間
イベント等に参加していただいていますし、大丈夫だろうと思っています。

3つ目の第12回勉強会@岡山に関しては以下のサイトをごらんください
募集も開始しています。今回は初めての発表の方とリベンジの方にお願いしています。
https://sites.google.com/site/chugokugtug/event/12

次に、確定していない将来の予定(目標)

  1. Slim3 Source Code Readingの完走
  2. 広島でのイベントの開催
  3. ハッカソン、ハンズオン
  4. Source Code Readingの開催をする。
1と4については、新しい試みとしてソースコードの読書会をやってみようと思っています。
個人的に読めば良いのですが、せっかくなので。(イベントの開催ルールのためもあったりなかったり)Slim3 Source Code Readingについては今の所、結果は残していますが、カウントしていません。

2の広島での開催については、Twitterで希望の意見があったので、それも反映したいと思っています。
※OSCへの参加に関しては、私自身が参加する必要があるかどうかをじっくり考えたいと思っています。場所を提供していただけるのは非常にありがたいですが、私に問題があるのか、あまり交流が深まらなかったように思いましたので。
(活動資金の面の問題が解決すれば良いのですが、そうも言ってられないので)

3のハッカソンに関してですが、今年は「競技プログラミング系」で攻めてみたいと
思っています。もしくは、「研究」という感じで、Googleの提供するAPIを
叩いてみるとか、ライブラリを作るみたいなやつ。

昨年の意見として、「アイデア、チームを決める時間が作れないと難しい」と
いう意見がありました。

という事で、それを反映しようと思うと「個人戦でアイデア(仕様)が決まっている
もの」となるのかなと。

それはそれで本来の意味のハッカソンとなるのでおもしろいんじゃないかと思っています。
ハンズオンについても、要望があったりするので、考えたいと思っています。

次に仕事関係
まず、専門学校の授業。
2月中旬ぐらいまでは授業があるので、その対応。
できれば、生徒達の未来を決める or より良いものにする事をしたい。

その先は、オファーがあってから決まることなので未定。

という事で、学校の授業以外で収入のために何かしないといけないのですが、

  1. 執筆する
  2. GAEアプリの開発
  3. Chrome-Extensionの開発
  4. Androidアプリの開発
  5. バイトする
  6. 何か仕事を受託する

上から順番にやりたい気持ちは強いです。

執筆については、今年も挑戦したいです。私の書籍に関してのフィードバックは
そんなに多くないですが、”今の所”、否定的な意見は見えないので
個人的に色々反映して執筆したいですね。

GAEアプリの開発に関しては、自分自身がサイト運営をした事がないのと、GAEの課金関連でのチューニングなどの情報を得るためにはどうしても動かしてみるしか解決策がないのでやってみようかなと。
Adsenseの情報も見たいですし。(どうすればお金になるのか。とか)

最初のテーマは「イベント管理」で、GAE/Gで作成しています。
データを保証しませんが、適当に使ってみてください。

http://eventmanapp.appspot.com/


※Googleアカウントを利用してイベント参加申し込みができます。
※メールアドレスが見える可能性があります。(一応先頭3文字で切ってる気がする)
※キャンセルができません。(すみません、未実装です)
※イベントの削除もできません(すみません、これも未実装です)
※リマインダ機能もありません(すみません、これも未実装です)
※CSS適用していません。
※スマートフォンで参照したことは一度もありません。
※Billing設定をいれていません。多分どこかでエラーになります。
※適当にデータが消える可能性があります。(本当にイベントが登録されたら内容を見て考えます)

中国GTUGでの発表のネタの為にGoで作成してみたのですが、
今の問題点はNickNameが取れないのでメールアドレスしか表示できないという
致命的な問題にぶつかっています。これはSDKのバージョンアップに期待するしか
なさそうです。

Chrome-Extensionの開発もChrome Web Storeが出ているわけですし、
HTML5/CSS/JavaScriptの勉強も兼ねてやってみたいと思っています。

Androidアプリの開発ですが、使っていただいているユーザもいらっしゃるのと、
個人的に最後にやってみたい事があるので、その研究をするために、新規で1本。
現状のアプリケーションの更新を行いたいと思っています。

個人的な意見ですが、Googleの技術を使って収入にするのは「難しい」と思います。
が、アイデア次第で収益に繋がる事は十分わかりました。
後は、”アイデア”か、”問題を解決するための機能”があれば、(生活できるとは限らないが)がんばれるのかなと。

それからバイトですね。先日、親にも「何してでも生きて行けるだろう」と言われたので
本当に苦しくなったらバイトしようかなと思っています。
どうせなら未経験なものをやりたいですね。(何事も経験だと思うので。と言ってもこの年で言うことか?とも思いますが)
ただ、こうなったら中国GTUGの活動は多少あきらめないといけなさそうです。

最後に受託するということですが、昨年は「未払い」に悩まされたのと、苦労だけして
(技術的な経験にはなったのですが、)失う物の方が大きかったので、
これには手を出さないと思います。(ある程度信用の置ける方で、規模、難易度と相談して、聞くかもしれませんが)

最後に生活面ですが、

  1. スポーツクラブに通う
  2. Google+を使う
1.は昨年も行っていましたが、今年も引き続き。
2.は昨年まではTwitterによく投稿していましたが、今年からGoogle+を利用してみたいと考えています。個人的に自分のストリーム(TwitterでいうTimeLine)は全部見たいと
思うのですが、Twitterで全員をフォローすると、流れてしまって見れないという
状況になってしまったので、ある日からフォロー数を制限していたのですが、
Google+だとサークルの概念があるので、(Twitterにもリストがある事をちょっと前に知った)切り替えれば見れるなと。後は、投稿に対するコメントは投稿の下にでるので、
replyを見逃さなくて良いかなとか。
あとは、短縮URLを作らなくても良いとか。(複数のURLが貼れない(?)けど)
Twitterは基本Rom専と告知、スマートフォンからの位置情報の投稿にしようと思います。

長文になってしまいましたが、今年もGoogle技術を追いかけるつもりです。
昨年Googleのサービスを利用してみたら、確かに生活は便利になったので。
便利な使い方は紹介したいですね。(押し付けるわけでなく)
それから、Webの技術を勉強しようと思うとちょうどいい感じで絡んでくるので。

2012年1月2日月曜日

bytes.NewBufferの使い方

Go言語プログラミング入門でもURLFetchAPIを利用した例で出てきたのですが、
掲載しているソースコードだとあまり良くないので再度使い方をまとめておきます。

import "bytes"

var writedata = make([]byte, 0, length)
var writebuf = bytes.NewBuffer(writedata)

書籍では、make([]byte, length)を使っていますが、この意味は、"長さと容量がlengthのバッファ"が作成されます。

bytes.NewBufferを利用する場合は、長さは0で、容量が必要なサイズに設定されている事が望ましいとされています。

したがって、"長さは0"というのを明示するために上記の例にしなければいけません。

実際に、GAE/G SDK 1.6.1で実行してみると、0埋めされたデータが出力されていました。
Buffer.Write([]byte)は容量が足りない場合は、バッファの容量を増やしてくれるように
なっているので、var writedata = make([]byte, 0)でも良いかもしれません。
この場合、容量が0なので、容量を内部的に増やす処理(新しいバッファを確保して上書きコピー)が実行されるため
実行コストが増えてしまいます。