2010年6月26日土曜日

RhodesConnectToWebServices

以下のドキュメントは、英語の原文を日本語へ翻訳したものをRhomobile社の許可のもと、公開しています。
公式なドキュメントはRhomobile社のサイトをご覧ください。
また、この文書は2010/6/16(JST)の情報がもとになっています。
この日本語の翻訳について問題・質問などがある場合、コメントを残してください。それ以外については、本ドキュメント内にある問い合わせ先にお願いします。

The following document (a translation of the English original into Japanese) is published with Rhomobile permission.
If you need official documents, please visit Rhomobile site.
In addition, the following document is based on 2010/June/16 (JST) information.
If you have any question, problem about the Japanese translation, please leave your comment. Except for that, refer contacts in the document.


ここでは、RhoSyncサーバーへの接続を有効にすることで、Rhodesアプリに同期データを追加する方法を説明します。同期は必須ではありませんが、rhogenで生成されたRhodesアプリはすべてメインページにsync loginがあります。このセクションの最後に示すように、RhodesでAsyncHttpライブラリを介してバックエンドWebサービスに直接アクセスすることもできます。

目次

ログイン/ログアウトマネージャ

Rhodesは内蔵ログインマネージャが付属しており、RhoSyncとの認証を処理します。次の関数で、ログインマネージャにアクセス可能です:

# Pre 1.2
SyncEngine::login(@params['login'], @params['password']) #returns 1 if login was successful to all sources, otherwise 0

SyncEngine::logout #performs a logout on each source
SyncEngine::logged_in #returns 1 if true, 0 if false

この使い方の例は、Rhodesアプリケーションの"Settings"コントロールを見てください。

Rhodes1.2以降ログインのコールバックを使用するようになっており、この機能は変更しています:

# Since 1.2 use this login method
SyncEngine.login(@params['login'], @params['password'], (url_for :action => :login_callback))

# Default callback that's provided by the generator
def login_callback
err_code = @params['error_code'].to_i
if err_code == 0
# run sync if we were successful
WebView.navigate Rho::RhoConfig.start_path
SyncEngine.dosync
else
if errCode == Rho::RhoError::ERR_CUSTOMSYNCSERVER
@msg = @params['error_message']
end

if !@msg || @msg.length == 0
@msg = Rho::RhoError.new(errCode).message
end

WebView.navigate ( url_for :action => :login, :query => {:msg => @msg} )
end
end

より詳細は、"System API Samples application"の/app/Settingsフォルダのコントローラとビューを見てください。

Sync notification

ソース上で同期が行われると、リフレッシュされるソースによって提供されるデータをビューが反映します。これを行うには、sync notificationメカニズムを使用してください。

notificationを設定するには、以下のRubyの呼び出しを行います:

  Account.set_notification(url,params)
SyncEngine.set_notification(source_id,url,params)
SyncEngine.set_notification(-1,url,params) #set callback for all sources

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

  Account.set_notification("/app/Account/sync_notify", "sync_complete=true")

Accountソースに関連したオブジェクトが変化すると、ユーザが同じページにアクセスしていると、ビューはsync_notify actionへ向けられます(パラメータに'sync_complate=true'が設定されています)。

また同期コールバックでは3つのステータスがあります:

  • それら3つに共通的なパラメータ:source_id、source_name
    • in_progress.パラメータ:total_count、processed_count、cumulative_count。これはユーザーに同期の進捗状況を報告するために使用することができます。
    • error.パラメータ:error_code、error_message
    • OK.パラメータ:total_count、processed_count、cumulative_count
  def sync_notify
status = @params['status'] ? @params['status'] : ""

if status == "error"
errCode = @params['error_code'].to_i
if errCode == Rho::RhoError::ERR_CUSTOMSYNCSERVER
@msg = @params['error_message']
else
@msg = Rho::RhoError.new(errCode).message
end

WebView.navigate(url_for(:action => :server_error, :query => {:msg => @msg}))
elsif status == "ok"

if SyncEngine::logged_in > 0
WebView.navigate "/app/Page"
else
# rhosync has logged us out
WebView.navigate "/app/Page/authenticate_form"
end
end
end

注:ビューがAjax呼び出しを使用して更新された場合は、このメカニズムが正常に働かないでしょう。ビューの場所が、Ajax呼び出しで別の場所へ変更されないからです。
Windows Mobileでの注意:set_notificationのパラメータ文字列は2024byteを超えないようしてください: http://support.microsoft.com/kb/208427

オブジェクト作成のエラー

サーバーがオブジェクトを作成中にエラーを返すことがあります。その場合、sync notificationコールバックが以下のパラメータを受け取ります:

create_error - ハッシュの配列です。各ハッシュは'オブジェクト'と'error_message'が含まれます。

Sync object notification

アプリケーションはsyncエンジンからオブジェクトの変更の通知を受けることができます。アプリケーションは、変更されたオブジェクトを表示する場合、現在のページを更新することができます。

  • SyncEngine.set_objectnotify_urlをコールする。たとえばAppApplication.initializeにおいて一度呼び出します。
  • コントローラメソッドで、レンダー・メソッドの前にオブジェクトの配列や一つのオブジェクトでadd_objectnotifyをコールします。
  • .erbファイルでurl_forを使う場合:ビューに特定のオブジェクトを選んだとき、そのurlはobject idを持っており、そのobject idは自動的にnotificationマップへ追加されます。
  • notificationコールバックは3つの配列を受け取ります。それはハッシュで:'deleted'、'updated'と'created'です。各ハッシュは'オブジェクト'と'source_id'のキー値を含みます。

class AppApplication < Rho::RhoApplication
def initialize
super

SyncEngine::set_objectnotify_url("/app/Settings/sync_object_notify")
end

end

class ProductController < Rho::RhoController

#GET /Product
def index
@products = Product.find(:all)

add_objectnotify(@products)
render
end
end

def sync_object_notify
#do something with notification data
WebView.refresh
end

画像/ブロブ同期

デバイスのカメラの画像を処理するモデルがソースのURLを提供する場合、RhoSyncサーバーへの画像の同期は自動的に行われます。

注:同じメカニズムは、画像だけではなくて、どんなファイルにも当てはまります。

バックエンドサーバーへのストアと同期

"Image"モデルがあり、Imageモデルのコントローラにカメラコールバック・ファンクションがあるとすると:

  def camera_callback
if @params['status'] == 'ok'

#create image record in the DB
image = Image.new({'image_uri'=>@params['image_uri']})
image.save
SyncEngine.dosync
end
#reply on the callback
render :action => :ok, :layout => false
end

このコールバックは、イメージが保存された後、同期をトリガします。そしてRhoSyncサーバへpushします。それは画像を処理するイメージのソースアダプタ次第です("blob"と呼ばれるpaperclipアタッチメントのように提供されます)。

  def create(name_value_list,blob)
if blob
obj = ObjectValue.find(:first, :conditions => "object = '#{blob.instance.object}' AND value = '#{name_value_list[0]["value"]}'")
path = blob.path.gsub(/\/\//,"\/#{obj.id}\/")
name = name_value_list[0]["value"]

`cp #{path} #{File.join(RAILS_ROOT,'public','images',name)}`
end
end

この例では、queryメソッドでイメージのリストをqueryできるように、blobをrails publicフォルダにコピーします。RhoSyncに付属の"camera"サンプルソースアダプタを見てください。

バックエンドサーバーからのファイル取得

新しいレコードを同期するときにデバイスが画像を取得できるために、クエリの呼び出しでは、新しいバックエンドの画像へのURLを格納する必要があります。

  def query
@result={}
Dir.entries(PATH).each do |entry|
new_item = {'image_uri' => BASEURL+'/images/'+entry, 'attrib_type' => 'blob.url'}
unless entry == '..' || entry == '.' || entry == '.keep'

p "Found: #{entry}"
@result[entry.hash.to_s] = new_item
end
end
@result
end

完全なrhosyncアダプタの例はrhosync/vendor/sync/SystemApiSamples/camera.rbを参照してください。

より詳細は、System API Samples applicationの/app/Blobフォルダのコントローラとビューを見てください。

非同期検索の呼び出し

サーバー上に大きなデータベースがある場合、デバイスへすべてをロードする必要はありません。代わりに選択された項目だけを非同期検索機能を使用して同期することができます:

コントローラのどこかに:

Contact.search(
:from => 'search',
:search_params => { :FirstName => @params['FirstName'], :LastName => @params['LastName'], :Company => @params['Company'] },
:offset => page*page_size,
:max_results => page_size,
:callback => '/app/Contact/search_callback',
:callback_param => "" )

Rhodes 1.4 注意:Rhodesは自動的にcallback_paramへsearch_paramsを追加しますので、コールバックでは:@['search_params'][:FirstName] などが利用可能です。

コールバックは次のようになるでしょう:

def search_callback    
if (status && status == 'ok')
WebView.navigate ( url_for( :action => :show_page, :query => @params['search_params']) )
end
#TODO: show error page if status == 'error'

render :action => :ok
end

findを早くするには:http://wiki.rhomobile.com/index.php/Rhom#find.28.2Aargs.29_Advanced_proposalを使います。
そして、コールバックが呼び出されデータが準備できたら、次のページをレンダリングすることがあります:

def show_page
$contacts = Contact.find(:all,
#:conditions => ["LOWER(FirstName) LIKE ? OR LOWER(LastName) LIKE ? OR LOWER(Company) LIKE ?",
# @params[:FirstName], @params[:LastName], @params[:Company]]

:conditions => {
{:func=>'LOWER', :name=>'FirstName', :op=>'LIKE'}=>@params[:FirstName],
{:func=>'LOWER', :name=>'LastName', :op=>'LIKE'}=>@params[:LastName],
{:func=>'LOWER', :name=>'Company', :op=>'LIKE'}=>@params[:Company],
},
:op => 'OR',

:select => ['FirstName','LastName', 'Company'],
:per_page => page_size, :offset => page*page_size )
render :action => :show_page
end

コールバックの呼び出しを中断するには、'stop'を返します:

def search_callback    
if (status && status == 'ok')
WebView.navigate ( url_for :action => :show_page )
end
#TODO: show error page if status == 'error'

'stop'
end

非同期searchコールパラメータ

Searchの呼び出しは、次のパラメータのハッシュを取ります。以下の名前以外のすべてのパラメータは、検索パラメータとみなされます。

:from #=> sets the path or custom method that records will be fetched from (optional, default is 'search')  
:offset #=> starting record to be returned
:max_results #=> max number of records to be returned
:callback #=> callback to be called after search is completed
:callback_param #=> parameters to be passed to the callback (optional)
progress_step#=>optional parameter, define how often search callback will be called with 'in_progress' state

上記の例からの呼び出し(Call)は、RhoSyncサーバーへの以下のような呼び出しになります:

search(検索) /search?conditions[FirstName]=Jon&conditions[LastName]=Smith&conditions[Company]=Acme,offset=30,max_results=10


SyncEngineクラス

同期エンジンクラスには呼び出し(Call)を追加できますが、その完全なセットを下記に記載します。

  • SyncEngine.dosync(show_sync_status)
    • リクエストは同期処理を開始します。オプションのパラメータshow_sync_statusがfalseの場合、同期ステータスポップアップが(デフォルトはtrueです)が表示されません
  • SyncEngine.dosync_source(source_id, show_sync_status)
    • 指定したソースの同期プロセスの開始要求です。オプションのパラメータshow_sync_statusがfalseの場合、同期ステータスのポップアップが(デフォルトはtrueです)が表示されません。
  • SyncEngine.lock_sync_mutex
    • 同期エンジンのロック(バッチ操作を実行するために便利)を取得するのを待ちます、
  • SyncEngine.unlock_sync_mutex
    • 取得した同期エンジンのロックを開放します(lockをしたら、これを行うこと)。
  • SyncEngine.login(login, password, callback)
    • syncサーバでユーザの認証を行います。コールバックURLはログイン操作が完了後に呼び出されます。さらなる詳細はここ
  • SyncEngine.logged_in
    • ユーザがsyncサーバによって認証されているなら、trueを返します。さらなる詳細はここ
  • SyncEngine.logout
    • Syncサーバーからユーザをログアウト。より詳細な情報はこちら
  • SyncEngine.stop_sync
    • 進行中の同期操作を停止します。
  • SyncEngine.set_notification(source_id, url, params)
  • SyncEngine.clear_notification(source_id)
  • SyncEngine.set_pollinterval(interval)
    • 同期のポーリング間隔を設定します。0は更新のためのポーリングを無効にしますが、同期はPUSH notificationによる起動が可能です。
  • SyncEngine.set_syncserver(syncserver)
    • syncサーバーのアドレスを設定しRho Configへ格納します。
  • SyncEngine.set_objectnotify_url
  • SyncEngine.set_pagesize
    • サーバーが見せるリクエストのページサイズを設定します。
  • SyncEngine.get_pagesize
    • サーバーが見せるリクエストのページサイズを取得します。

Webサービスの利用

バックエンドアプリケーションに接続するためにRhoSyncを使用する必要はありません。http経由で直接、バックエンドのWebサービスを呼び出すことができます。具体的にはAsyncHttpライブラリです。httpsを処理するには、httpsをURLの頭に付けるだけです。認証を処理するには、ヘッダーのハッシュを適切に設定するだけです。Webサービスから返却されるXMLやJSONを処理するためには、普通はJSONやRexmlライブラリをSupported extensionsで述べたようにloadしなければなりません。

AsyncHttp

Webサービスやその他のhttp(s)サーバへ非同期呼び出しを行うには、Rho::AsyncHttpを使うべきでしょう。

  • get(:url, :headers, :callback,:callback_params)
  • post(:url, :headers, :body, :callback,:callback_params)
  • download_file(:url, :headers, :filename, :callback, :callback_params)
  • upload_file(:url, :headers, :filename, :body, :callback, :callback_params)
  • cancel(cancel_callback = '*') # cancel current http call, '*' is by default, means cancel all current http calls
  Rho::AsyncHttp.get(
:url => 'http://www.example.com',
:headers => {'Cookie' => cookie},
:callback => (url_for :action => :httpget_callback),
:callback_param => "" )

Rho::AsyncHttp.post(
:url => 'https://www.example.com',
:headers => {'Cookie' => cookie},
:body => 'Test',
:callback => '/app/Contact/httpget_callback',
:callback_param => "" )

@@file_name = File.join(Rho::RhoApplication::get_base_app_path(), 'test.jpg')
Rho::AsyncHttp.download_file(
:url => 'http://rhomobile.com/wp-content/themes/rhomobile/img/imgs_21.jpg',
:filename => @@file_name,
:headers => {},
:callback => (url_for :action => :httpdownload_callback),
:callback_param => "" )

@@file_name = File.join(Rho::RhoApplication::get_base_app_path(), 'rhoconfig.txt')
Rho::AsyncHttp.upload_file(
:url => 'http://dev.rhosync.rhohub.com/apps/SystemApiSamples/sources/client_log?client_id=19bdcf15-aca2-4e5a-9676-3c297c09bb11&device_pin=& log_name=',
:filename => @@file_name,
:body => "" #body will be sent with as separate multipart
:headers => {},
:callback => (url_for :action => :httpupload_callback),
:callback_param => "" )

def httpget_callback
#@params contain response. In case of json (ContentType=application/json) @params['body']
#contain parsed body represented as hash of hashes
#@params['headers'] contain response headers represented as hash
#@params['cookies'] contain parsed cookies suitable for server request
#in case of unrecognized body type @params['body'] contain raw text body
#in case of error usual RhoError info returned plus @params['http_error'] and @params['body'] contain server response
end

注:新しいパラメータ :ssl_verify_peerがRhodes2.0(デフォルトはtrue)からあります。それはAsyncHttpは自己署名証明書を持つサイトで動作できるよう、リモートピアの検証を無効にすることができます。

注:Rhodes2.0から、AsyncHttpは:authorizationパラメータをサポートしています。今のところ'ベーシックHTTP認証'がサポートされてます。使用例:

RRho::AsyncHttp.get(
:url => 'http://www.example.com',
:callback => (url_for :action => :httpget_callback),
:authorization => {:type => :basic, :username => 'username', :password => 'none'} )

注: XMLをパースするのにrexml ruby extensionを使用します:http://wiki.rhomobile.com/index.php/Rhodes#rexml_library_support
例はrhodes-system-api-samples\AsyncHttpを参照してください。

注: 同期呼び出しをするには、callbackパラメータをスキップするだけです(1.5.1以降)

    res = Rho::AsyncHttp.get( :url => 'http://www.apache.org/licenses/LICENSE-2.0')
puts "Sync http call: #{res}"

@@get_result = res['body']

バックエンドWebサービスへ直接接続する例

ここにRexml sample in System API Samplesのコントローラがあります。これは、テストのWebサービスへのAsyncHttp.getを呼び出します。その後、Rexmlで戻りをパースします。

   def webservicetest
Rho::AsyncHttp.get(
:url => 'http://rhostore.heroku.com/products.xml',
:callback => (url_for :action => :httpget_callback),
:callback_param => "" )

render :action => :wait
end

def get_res
@@get_result
end

def get_error
@@error_params
end

def httpget_callback
puts "httpget_callback: #{@params}"


if @params['status'] != 'ok'
@@error_params = @params
WebView.navigate ( url_for :action => :show_error )
else
@@get_result = @params['body']
puts "@@get_result : #{@@get_result}"

begin
require 'rexml/document'


doc = REXML::Document.new(@@get_result)
puts "doc : #{doc}"
rescue Exception => e
puts "Error: #{e}"
@@get_result = "Error: #{e}"
end

WebView.navigate ( url_for :action => :show_result )
end

end

def show_result
render :action => :webservicetest, :back => '/app/RexmlTest'

end


0 件のコメント: