Murayama blog.

プログラミング教育なブログ

Parse - Cloud Code - Getting Started

What is Cloud Code?

Parseのビジョンは、モバイルアプリを開発するデベロッパーから、サーバーの取り扱いをなくすことです。複雑なアプリケーションの場合は、ときとしてモバイルデバイス上で実行できないロジックが必要になります。Cloud Codeはこのような課題を解消します。

Cloud Codeの使い方は簡単です。なぜなら、アプリの開発で利用してきたJavaScript SDKをそのまま利用できるからです。モバイルデバイス上で動作するのか、Parse Cloud上で動作するのかという違いだけです。Cloud Codeはアップデートすると、モバイル環境へすぐに反映できるので、アプリケーションのリリースを待つ必要がなくなります。これにより、アプリケーションの更新が簡単になり、新機能も素早く追加できようになるでしょう。

あなたがモバイル開発に熟知しているとしても、Cloud Codeの理解しやすさ、簡単さを実感してもらえることでしょう。

Getting Started

開発用のコンピュータにParseコマンドラインツールをインストールしましょう。これにより、Parse Cloud上のコードを管理できるようになります。

Installing or Updating the Command Line Tool (Mac/Linux)

Mac OSLinux/Unix環境であれば次のコマンドでインストールできます。

curl -s https://www.parse.com/downloads/cloud_code/installer.sh | sudo /bin/bash

上記のコマンドを実行すると/usr/local/bin/parseが作成されるので、"parse"コマンドを利用できるようになります。その際、余計なファイルは生成されませんので、アンインストールしたい場合は/usr/local/bin/parseを削除するだけです。また、既にインストール済みのコマンドをアップデートするこもできます。

Installing the Command Line Tool (Windows)

Window用のコマンドラインツールはこちらにあります。zipファイルをダウンロード、解凍した後、ParseConsole.exeをダブルクリックするとpowershellセッションが起動します。ParseConsoleが起動したらWin + R と“parse”コマンドでpowershellセッションを開始します。*1

Setting Up Cloud Code

次のステップは、クラウド上で実行するコードを格納するためにディレクトリを作成します。parse newコマンドはディレクトリをセットアップし、作成対象のアプリケーションの選択を要求します。

$ parse new MyCloudCode
Email: ninja@gmail.com
Password:
1:MyApp
Select an App: 1
$ cd MyCloudCode

Parseのログインに必要なEメールアドレスとパスワードを入力します。OAuth経由でサインアップしてパスワードをセットしたことがない場合、アカウントの設定をその場で編集する必要があります。以上でカレントディレクトリ上にMyCloudCodeディレクトリが作成されます。ディレクトリの中にはいくつかのファイルが自動生成されています。

-config/
  global.json
-cloud/
  main.js
-public/
  index.html

configディレクトリは、JSON形式の設定ファイルが格納されています。たいていは、このファイルを編集することはありません。cloudディレクトリにはCloud Codeが格納されており、publicディレクトリにはParse上でホストする静的コンテンツが格納されています。cloudディレクトリのmain.jsにCloud Codeファンクションを記述します。現時点では、正しくファイルが生成されていることを確認しておいてください。もし、ソース管理を行うなら、これらのファイルはすべてチェックインすることができます。

A Simple Function

古代からの伝統に従い、クラウド上で動作する最もシンプルなファンクションを実行してみます。cloud/main.jsを開くと、文字列を返すサンプルを確認できます。

Parse.Cloud.define("hello", function(request, response) {
  response.success("Hello world!");
});

このコードをあなたのマシンからParse Cloudにデプロイするには、次のコマンドを実行します。

$ parse deploy

デプロイが完了したら、次のコマンドを実行してみましょう。

 curl -X POST \
  -H "X-Parse-Application-Id: Your-Parse-Application-Id" \
  -H "X-Parse-REST-API-Key: Your-Parse-Rest-Api-Key" \
  -H "Content-Type: application/json" \
  -d '{}' \
  https://api.parse.com/1/functions/hello

次のようなレスポンスを確認できるでしょう。

{
  "result": "Hello world!"
}

おめでとうございます。デプロイが完了し、Cloud Codeも正しく動作しています。

開発のサイクルを確認する良い機会ですので、"Hello world!"を他の文字列に変更しデプロイと実行を試してみてください。異なる結果が返ることを確認しましょう。JavaScript SDKのすべてをCloud Code上で利用できるので、できることはたくさんあります。以降、いくつかのサンプルを見ていくことにします。

*1:powershellは使ったことがないです。。

AndroidでParse入門 - まとめ的な -

これはなにか

流行のParseの勉強をしたかったので、本家のAndroidドキュメントを読みながら勉強した記録です。
Android Developer Guide | Parse

翻訳?

英語はできないので雰囲気で訳してます。*1

*1:間違えてたらごめんなさい

AndroidでParse入門 - Security -

元ネタ

こちらを参考にSecurityについてまとめます。
https://parse.com/docs/android_guide#security-recommendations

前回の記事はこちら。
AndroidでParse入門 - Handling Errors - - Murayama blog.

Security

Parseはアプリケーションの構築において可能な限りデータアクセスを制限することを強く推奨しています。具体的にはアプリケーションの初期化時に、Anonymous Userの作成Current UserへのデフォルトACLの適用を定義することを推奨しています。また、データを不正アクセスから守るために、個々のオブジェクトにPublicな書き込み制限(および、読み込み制限)を明示的に設定するようにします。

アプリケーションのスタートアップ処理の中で次のように実装します。

ParseUser.enableAutomaticUser();
ParseACL defaultACL = new ParseACL();
// Optionally enable public read access while disabling public write access.
// defaultACL.setPublicReadAccess(true);
ParseACL.setDefaultACL(defaultACL, true);

データへのセキュアなアクセスを保つことを念頭において、あなたとユーザの両方を保護するアプリケーションを構築してください。

Settings

セキュアなコーディングに加えて、アプリケーションの設定画面においても、アプリケーションへのアクセス制限が適切な設定となっているか確認してください。

たとえば、Facebookアカウントなしでログインできないように設定するなら、その他のログインメカニズムを無効にしておくべきです。指定したFacebookアプリケーションID、Twitterコンシューマキーといった情報は、ユーザのログインの都度サーバサイドでの検証が有効となっています。

AndroidでParse入門 - Handling Errors -

元ネタ

こちらを参考にHandling Errorsについてまとめます。
https://parse.com/docs/android_guide#errors

前回の記事はこちら。
AndroidでParse入門 - User Interface - - Murayama blog.

Handling Errors

ParseObjectのsave()、delete()、get()といった多くのメソッドは、削除、編集すべきデータが存在しない場合や、ネットワーク通信の不具合などによって、リクエストを処理できなければParseExceptionをスローします。そのような場合、必要に応じてアプリケーション内でこれらの例外をキャッチし例外処理を施す必要があります。

詳細についてはAndroid APIを参照してください。

AndroidでParse入門 - User Interface -

元ネタ

こちらを参考にUser Interfaceについてまとめます。
https://parse.com/docs/android_guide#ui

前回の記事はこちら。
AndroidでParse入門 - GeoPoints - - Murayama blog.

User Interface

アプリケーションのユーザはAndroidのUIコンポーネントと対話します。ParseはParse上のデータを効率よく扱うための共通的なUIウィジェットのサブクラスを提供しています。

ParseQueryAdapter

Parseはコレクションデータを表示するためにAdapterの実装クラスを提供しています。静的なオブジェクト配列を使うベーシックなListAdapterを使う代わりに、ParseQueryAdapterクラスを使えば抽象的なレイヤーが提供されます。その結果、AdapterView(たとえばListView、GridView)を使って、Parseクラスのデータを簡単に表示できるようになります。

Activity上でデータを表示するためのParseQueryAdapterの使い方は、onCreateメソッドで次のように実装します。

  1. ParseQueryAdapterを生成します。
  2. 必要に応じてカスタマイズします(データの問い合わせ、ビューの変更などの詳細については、後のセクションを参照してください)
  3. setAdapter()メソッドによってAdapterViewにAdapterをセットします。

WindowにAdapterViewがアタッチされると、ParseQueryAdapterは自動的にデータの先頭部分をフェッチします。このサブクラスは次に示すコードを簡略化します。

  1. タップで次のページを読み込むページネーション
  2. 行内のリモートイメージの表示やダウンロードの設定
  3. Parseオブジェクト配列の自動読み込み管理
  4. データライフサイクルにおける主要なイベントからのコールバック

次のコードは、ListViewにデータを表示するためのシンプルなParseQueryAdapterのセットアップです。わずかな設定で機能的なListViewが実装できます。

// Inside an Activity
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  // Uses a layout with a ListView (id: "listview"), which uses our Adapter.
  setContentView(R.layout.main);
 
  ParseQueryAdapter<ParseObject> adapter = new ParseQueryAdapter<ParseObject>(this, "Instrument");
  adapter.setTextKey("name");
  adapter.setImageKey("photo");
 
  ListView listView = (ListView) findViewById(R.id.listview);
  listView.setAdapter(adapter);
}

このビューはInstrumentsリストのnameを表示します。ここでは記述していないコードに注目してください。連続したページのフェッチロジックを記述していませんし、配列データの管理ロジックも記述していません。UIウィジェットに表示するイメージをバックグラウンドでダウンロードするコードもないですし、次のページをロードするタッチイベントの処理も記述していません。

ParseQueryAdapterは、データの加工や表示方法、フェッチ時の前処理、後処理をカスタマイズできます。以降の節では、ParseQueryAdapterを使ってあなたが何をできるのかを解説します。ニーズに合わせるために、どのように微調整すれば良いかを見ていきましょう。

Customizing the Query

ParseQueryAdapterのコンストラクタは、ContextとParseのクラス名を引数に取ります。これにより、クラスに属するすべてのParseObjectがフェッチされ、createdAtタイムスタンプ順に表示されます。

Parseは、この振る舞いを変更できるようにArrayAdapterの機能から設計しています。ParseQueryAdapterは、Adapterによって表示されるオブジェクト配列を設定するのではなく、自分で定義したParseQueryを返すQueryFactoryクラスを引数に受け取ります。QueryFactoryインスタンスコンストラクタで受け取ることで、Adapterはクエリの結果を使用し、オブジェクトをフェッチして表示するようになります。

次のコードは、punkとmetalで4名以上メンバーがいるバンドをレコードの売上順で表示するためのParseQueryAdapterのセットアップです。

ParseQueryAdapter<ParseObject> adapter =
  new ParseQueryAdapter<ParseObject>(this, new ParseQueryAdapter.QueryFactory<ParseObject>() {
    public ParseQuery<ParseObject> create() {
      // Here we can configure a ParseQuery to our heart's desire.
      ParseQuery query = new ParseQuery("Band");
      query.whereContainedIn("genre", Arrays.asList({ "Punk", "Metal" }));
      query.whereGreaterThanOrEqualTo("memberCount", 4);
      query.orderByDescending("albumsSoldCount");
      return query;
    }
  });

Customizing the Rows

AdapterView内の個々ビューのデフォルトレイアウトは、ParseImageViewとTextViewによるシンプルなLinearLayoutです。ParseQueryAdapterに対してsetTextKey(String) を呼び出すと、指定したキーがParseObjectの表示項目となり、TextViewに表示されます。同様に、setImageKey(String)を呼び出すと、指定したキーがParseObjectの表示項目となり、ImageViewに表示されます。

行をカスタマイズする一つの方法は、getItemView(ParseObject, View, ViewGroup) やgetNextPageView(View, ViewGroup)をオーバーライドし、スーパークラスの適切なメソッドを呼び出す方法です。独自のレイアウトをスーパークラスの実装に提供する場合、getItemView(ParseObject, View, ViewGroup) とgetNextPageView(View, ViewGroup) は、textKeyがセットされているならTextView (id: android.R.id.text1)、imageKeyがセットされているならParseImageView (id: android.R.id.icon)を期待します。*1

独自のレイアウトをinflateして設定し、TextViewとParseImageView、さらに"description"TextView(id: R.id.description)を追加する場合、次のようになります。

@Override
public View getItemView(ParseObject object, View v, ViewGroup parent) {
  if (v == null) {
    v = View.inflate(getContext(), R.layout.adapter_item, null);
  }
 
  // Take advantage of ParseQueryAdapter's getItemView logic for
  // populating the main TextView/ImageView.
  // The IDs in your custom layout must match what ParseQueryAdapter expects
  // if it will be populating a TextView or ImageView for you.
  super.getItemView(object, v, parent);
 
  // Do additional configuration before returning the View.
  TextView descriptionView = (TextView) v.findViewById(R.id.description);
  descriptionView.setText(object.getString("description"));
  return v;
}

行をカスタマイズするもう一つの方法は、ParseQueryAdapterのメソッドをオーバーライドし、スーパークラスの実装を完全に無視する方法です。ParseObjectに定義されたcolorを個々のItem Viewに適用するシンプルな例は次のようになります。

@Override
public View getItemView(ParseObject object, View v, ViewGroup parent) {
  if (v == null) {
    v = View.inflate(getContext(), R.layout.adapter_item, null);
  }
  v.setBackgroundColor(object.getInt("color"));
  return v;
}
 
@Override
public View getNextPageView(View v, ViewGroup parent) {
  if (v == null) {
    // R.layout.adapter_next_page contains an ImageView with a custom graphic
    // and a TextView.
    v = View.inflate(getContext(), R.layout.adapter_next_page, null);
  }
  TextView textView = (TextView) v.findViewById(R.id.nextPageTextViewId);
  textView.setText("Loaded " + getCount() + " rows. Get more!");
  return v;
}

Loading Remote Images in Rows

ParseQueryAdapterは、簡単にリモートイメージを表示することができます。setImageKey(String)を呼び出せば、ParseFileを保持したParseObjectのキー名を通じて、Parseからイメージをフェッチし対象行のParseImageViewにロードします。

イメージは非同期でダウンロードされるので、ParseImageViewはバックグラウンドで更新されます。ユーザのスクロールにより、Adapterによって行がリサイクルされると、ParseObjectが割り当てられ、イメージがフェッチされて表示されます。

画像のフェッチが完了するまでプレースホルダイメージを定義することもできます。ParseQueryAdapterのsetPlaceholder(Drawable) を呼び出すと、指定したDrawableがフォールバックイメージとして描画されます。

Lifecycle Methods

カスタムロジックを適用するために、Adapterに2つのデータライフサイクルフックを用意しています。一つは、データのクエリの前処理であり、もう一つはクエリからフェッチされたオブジェクトがロードされたときの後処理です。これらのメソッドは、ロードされたUIの切り替えなどに有効です。OnQueryLoadListenerはsetOnQueryLoadListenerによってセットします。onLoading() とonLoaded(List, Exception) を実装する必要があります。

Pagination

ページネーションは、テーブルからリストの1ページ目だけを取得します。ページに含むオブジェクトの件数は、setObjectsPerPage(int)を呼び出すことで指定できます。

クエリはページネーションによって自動的に調整されます。AdapterViewの最終行にページネーション行が表示されるので、ユーザは次のページを読み込むことができます。

ページネーションはデフォルトで有効となっています。無効にしたい場合はsetPaginationEnabled(false)を呼び出してください。ページネーションを無効にすると、ParseQueryAdapterはデフォルトで最大100件の結果を読み込みます。

Auto-loading of Data

ParseQueryAdapterを設定したAdapterViewがWindowにアタッチされると、先頭のページをフェッチする際にParseQueryAdapterのloadObjects()メソッドが呼び出されます。この挙動を無効にしたい場合(データの読み込みを遅延させたり、カスタムロジックを実行させたりしたい場合)、setAutoload(false)を呼び出してください。その場合、loadObjects()は手動で呼び出すようにします。

*1:変な言い回しですみません、後のコード見た方が良いです。

AndroidでParse入門 - GeoPoints -

元ネタ

こちらを参考にGeoPointsについてまとめます。
https://parse.com/docs/android_guide#geo

前回の記事はこちら。
AndroidでParse入門 - Cloud Functions - - Murayama blog.

GeoPoints

Parseのオブジェクトは緯度・経度といった位置情報をサポートしています。ParseObjectにParseGeoPointを追加することで、指定した位置情報の近くにあるオブジェクトを問い合わせることが可能となります。この仕組みを使う事で、あるユーザの近くにいる人を見つけたり、ある場所の近くにいるユーザを見つけたりといったことが簡単にできるようになります。

ParseGeoPoint

オブジェクトに位置情報を関連づけるにはParseGeoPointを作ります。緯度40.0、経度-30.0を指定するなら次のようになります。

ParseGeoPoint point = new ParseGeoPoint(40.0, -30.0);

この位置情報は、オブジェクトのフィールドに保存できます。

placeObject.put("location", point);

Geo Queries

位置情報を持つオブジェクトを多数用意できたなら、ある地点から最も近いオブジェクトを見つけることができます。これにはParseQueryのwhereNearメソッドを制約として使います。10件の場所のリストを取得する場合は次のようになります。

ParseGeoPoint userLocation = (ParseGeoPoint) userObject.get("location");
ParseQuery<ParseObject> query = ParseQuery.getQuery("PlaceObject");
query.whereNear("location", userLocation);
query.setLimit(10);
query.findInBackground(new FindCallback<ParseObject>() { ... });

useLocationの地点から、距離の近い順でリストオブジェクトが返却されます。ただし、orderByAscending()/orderByDescending()を指定した場合は、距離の順序よりも優先されます。

距離を使って結果を制限したい場合は、whereWithinKilometers、whereWithinMiles、whereWithinRadiansメソッドを使用してください。

特定のエリアに含まれるオブジェクトを問い合わせることもできます。ある矩形の範囲内に含まれるオブジェクトを探したいなら、ParseQueryのwhereWithinGeoBoxメソッドを使います。

ParseGeoPoint southwestOfSF = new ParseGeoPoint(37.708813, -122.526398);
ParseGeoPoint northeastOfSF = new ParseGeoPoint(37.822802, -122.373962);
ParseQuery<ParseObject> query = ParseQuery.getQuery("PizzaPlaceObject");
query.whereWithinGeoBox("location", southwestOfSF, northeastOfSF);
query.findInBackground(new FindCallback<ParseObject>() { ... });

Caveats

いくつか注意事項があります。

  1. 個々のParseObjectクラスには、ParseGeoPointオブジェクトを一つのキーでしか保持できません。
  2. 位置情報は、範囲の上限を超えてはいけません。緯度は-90.0から90.0、経度は180.0から180.0を超えてはいけません。範囲外の値を設定するとエラーとなります。

AndroidでParse入門 - Cloud Functions -

元ネタ

こちらを参考にCloud Functionsについてまとめます。
https://parse.com/docs/android_guide#cloudfunctions

前回の記事はこちら。
AndroidでParse入門 - Twitter Users - - Murayama blog.

Cloud Functions

Cloud Functionsは、ParseCloudによってAndroidから呼び出すことができます。たとえば、helloという名前のCloud Functionを呼び出すなら次のようになります。

ParseCloud.callFunctionInBackground("hello", new HashMap<String, Object>(), new FunctionCallback<String>() {
  void done(String result, ParseException e) {
    if (e == null) {
      // result is "Hello world!"
    }
  }
});

Cloud Functionsについて詳しく知りたいならCloud Code Guideを参照してください。