Dangoチュートリアル

プロジェクトの作成

Railsプロジェクト「chat」を作成します。

rails chat


※ 複数のRailsがインストールされている場合、

rails _2.1.1_ chat

と入力することで、Rails 2.1.1のプロジェクトを作成できます。

dangoの初期設定

dangoの初期設定を行います。

cd chat
ruby script/generate dango init

サーバー側プログラムの作成

「chat/dango/server」フォルダ内の「99_dango_server.rb」を、テキストエディタ等で開きます。
「script/generate dango init」実行直後の「99_dango_server.rb」は、以下のようになっています。

=begin
= dangoサーバー
=end

# メインクラス
class dangoServer < dangoServerFramework
  # サーバー起動時のインスタンス変数などを定義するメソッド
  # ここで定義されるインスタンス変数はサーバー全体で共有される
  def dango_server_init()
  end

  # 接続時に呼び出されるメソッド
  def dango_connect() 
  end

  # 接続解除時に呼び出されるメソッド
  def dango_close() 
  end
end

ここにある3つのメソッドは、これから作成するdangoサーバーのベースになるものです。

dango_server_init()メソッド
サーバーの初期化メソッドです。
dango_server_init()メソッドは、dangoサーバーの起動時に1度だけ実行されます。
dango_connect()メソッド
Flashで作られたdangoクライアントとの接続開始時に呼び出されるメソッドです。
複数クライアントとの通信時に、個々のクライアントを特定するためのオブジェクト生成などはここで行います。
dango_close()メソッド
Flashで作られたdangoクライアントとの接続終了時に呼び出されるメソッドです。
クライアント特定用オブジェクトの破棄などはここで行います。


今回はチャットを作る事が目的ですので、まずはクライアント特定用オブジェクトの発行と破棄の仕組みを作ります。
まずは、サーバー起動時に、クライアント特定用のオブジェクトを保存しておくための配列を作成します。

# サーバー起動時のインスタンス変数などを定義するメソッド
# ここで定義されるインスタンス変数はサーバー全体で共有される
def dango_server_init()
  shared[:users] = []
end

sharedは、dangoサーバー起動時に自動的に生成される、ハッシュに似たオブジェクトです。
shared内にあるデータは、読み書きの際に排他処理(トランザクション処理)をかけることが可能です。

次に、クライアント接続時にクライアント特定用オブジェクトをsharedに保持するよう、コードを追加します。

# 接続時に呼び出されるメソッド
def dango_connect() 
  shared.transaction(:users) do |users|
    users.push(session[:sid])
    shared.commit(users)
  end
end

shared.transaction(key)メソッドは、sharedオブジェクト内の特定キーを持つデータに対し排他処理を行います。
今回は、dango_server_init()メソッドで作成したshared[:users]に排他処理を行っています。

session[:sid]は、クライアントからのHTTPアクセス開始時にdangoが自動的に割り振っている任意のIDです。
dangoサーバーでは、このsession[:sid]を利用してクライアントの判別を行います。

最後に、クライアントとの接続解除時にクライアント特定用オブジェクトを破棄するよう、コードを追加します。

# 接続解除時に呼び出されるメソッド
def dango_close() 
  shared.transaction(:users) do |users|
    users.delete(session[:sid])
    shared.commit(users)
  end
end

クライアント側(Flex)の作成

今回、クライアントの作成にはFlexを使用します。
※ 以下の説明は、Flex及びFlex Builderがインストールされている事が前提です。

Flex Builderを起動し、新規Flexプロジェクトを作成します。
Flex_1
【画面】 Flexプロジェクトの新規作成
プロジェクトの形式を選択するよう求められますので、「ベーシック」を選択します。
Flex_2
【画面】 Flexプロジェクトの新規作成(プロジェクト形式選択)
プロジェクト名及びプロジェクトの保存場所の入力を求められます。
今回は、以下のように入力します。
プロジェクト名: SampleChat
プロジェクトの保存フォルダ: chat/dango/client_swf Flex_3
【画面】 Flexプロジェクトの新規作成(プロジェクト名/プロジェクト保存場所入力)
Flexのデザインビューを表示させ、「TextInputコンポーネント」を配置します。
配置したTextInputコンポーネントのIDプロパティ欄に「text_input」と入力します。
このコンポーネントに、チャットの発言内容を入力します。 Flex_4
【画面】 TextInputコンポーネントの配置
「TextAreaコンポーネント」を配置します。
配置したTextAreaコンポーネントのIDプロパティ欄に「text_area」と入力します。
このコンポーネントに、チャットのログが表示されます。 Flex_5
【画面】 TextAreaコンポーネントの配置
「Buttonコンポーネント」を配置します。
配置したButtonコンポーネントのクリック時プロパティ欄に「click()」と入力します。
このButtonコンポーネントをクリックした時に、text_input内のテキストがサーバーに送信されるようにします。 Flex_6
【画面】 Buttonコンポーネントの配置
これで、画面作成は終了です。
次にFlexのソースビューを表示させ、ActionScriptを記述していきます。 Flex_7
【画面】 ソースビュー
続いて、このアプリケーション実行時に必要となる各関数を記述していきます。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="app_complete();">
  <mx:TextInput x="10" y="10" id="text_input"/>
  <mx:TextArea x="10" y="40" width="416" height="350" id="text_area"/>
  <mx:Button x="178" y="10" label="送信" click="click();"/>
  
  <mx:Script>
    <![CDATA[
      private var sample_chat_base:SampleChatBase;
      
      //dango通信用オブジェクト初期化
      private function app_complete():void {
        sample_chat_base = new SampleChatBase(this);
      }
      
      //送信ボタンクリック時の処理
      private function click():void {
        sample_chat_base.send_message(text_input.text);
        text_input.text = '';
      }
      
      //サーバーから届いたチャットログをテキストエリアに追加する
      public function add_text_log(add_text:String):void {
        text_area.text = add_text + "\n" + text_area.text;
      }
    ]]>
  </mx:Script>
</mx:Application>

app_complete()メソッド
dangoを使って通信を行う際に必要なオブジェクトを作成します。
app_complete()メソッドはFlexの読み込みが完了した際に実行する必要がありますので、 <mx:Application>タグの属性として、「applicationComplete="app_complete();"」を追加します。
click()メソッド
送信ボタンクリック時に、text_input内のテキストをサーバーに送信します。
app_complete()メソッドで作成してあるSampleChatBaseインスタンスのsend_messageメソッドを呼び出します。
add_text_log()メソッド
サーバーから届いたチャットログをテキストエリアに追加します。

クライアント側(Flex)の作成 - SampleChatBaseクラス

実際にクライアントからサーバーへの通信を行うSampeChatBaseクラスの実装を行います。
FlexBuilderで、新規「ActionScriptクラス」ファイルを作成します。 Flex_8
【画面】 新規「ActionScriptクラス」ファイルの作成
作成する「ActionScriptクラス」の名前は、「SampleChatBase」とします。 Flex_9
【画面】 「SampleChatBase」クラスの作成
正しくファイルが作成されたら、SampleChatBaseクラスの中身を記述していきます。

package {
  import flash.events.*;
  import flash.display.*;
  import mx.utils.ObjectUtil;
  import org.rubyforge.dango.*;
  
  public class SampleChatBase {
    private var sprite_app:Object;
    private var dango_client:dangoClientFramework;
    
    //コンストラクタ
    public function SampleChatBase(t:Object) {
      sprite_app = t;    // tは、呼び出し元のFlex自身
      
      //dangoの接続処理
      var config:dangoConfig = new dangoConfig();    // dangoの設定情報(接続先サーバー情報)を読込
      try {
        dango_client = new dangoClientFramework(config);    // サーバーとの接続
      } catch(err:dangoError) {
        trace("接続エラー:failed to socket initialize.");
      }
      
      //エラー時のイベントリスナ
      dango_client.addEventListener("dangoError", connection_error);
      
      //データ受信時のイベントリスナーの追加
      dango_client.addEventListener("dango_notice_message", dango_notice_message);
    }
    
    //接続エラー
    private function connection_error(evt:dangoErrorEvent):void {
      trace("FrameworkError:code=" + evt.code + ":message=" + evt.message);
    }
    
    //チャット送信処理
    public function send_message(message:String):void {
      if(message == "") { return; }
      var send_obj:Object = { "message":message };
      try {
        dango_client.send_action("send_message", send_obj);
        trace("送信:" + ObjectUtil.toString(send_obj));
      } catch(err:dangoError) {
        trace("データ送信失敗");
      }
    }
    
    //データ受信 dango_notice_message
    private function dango_notice_message(evt:dangoReceiveEvent):void {
      trace("dango_notice_message");
      var ret:Object = evt.receive_data;
      sprite_app.add_text_log(ret["message"]);
    }
  }
}

コンストラクタ SampleChatBase()
コンストラクタでは、dangoサーバーへの接続処理と、エラー時及びデータ受信時のイベントリスナー追加を行います。
connection_error()メソッド
dangoサーバーとの通信エラーが発生した場合、connection_error()メソッドが呼ばれます。
今回のプロジェクトでは、通信エラーが発生した場合には、エラーコードとメッセージがtraceに出力されるようになっています。
send_message()メソッド
デザインビューで作成したtext_inputオブジェクト内のテキストをサーバーに送信します。
テキストが空でない場合のみ、サーバーにデータを送信します。

dango_client.send_action("send_message", send_obj);
では、dangoサーバーの"send_message"に対し、send_objを送信しています。
dango_notice_message()メソッド
サーバーから送信されてきたチャットログを取得し、text_area内のデータを更新します。

これで、クライアント側は完成です。
プロジェクトのビルドを行います。 Flex_10
【画面】 プロジェクトのビルド
ビルドされたデータは、chat/dango/client_swf/bin に出力されます。
このデータに、ブラウザからアクセスできるよう、binフォルダをchat/publicにフォルダごとコピーします。

サーバー側の作成

再び、サーバー側の作成を行います。
クライアント作成時、サーバー側に通信するための処理中に
dango_client.send_action("send_message", send_obj);
という記述がありました。
サーバー側では、これに"dango_receive_"を付加し、「dango_receive_send_message」という名称でメソッドを作成します。
このメソッドでは、送られてきたメッセージを受信し、 参加中の全ユーザーに対しメッセージを配信すれば良いので、コードは以下のようになります。

# メッセージを受信して、全員に送信
def dango_receive_send_message(rec_obj)
  send_obj = {:message => rec_obj["message"]}
  send_notice(:notice_message, shared[:users], send_obj)
end

send_noticeは、第一引数が任意の送信内容名、第二引数が送信先、第三引数が送信内容になっています。

プロジェクトの実行

Rails及びdangoサーバーを実行します。
「コマンド プロンプト」ウィンドウを新たに2つ表示させ、それぞれのウィンドウで以下を入力します。

cd chat
ruby script/server webrick -p 80

cd chat
ruby scriptdango_server

※ rails/server/dangoの中身を書き換えた場合には、dangoの再起動が必要です。(railsの再起動は不要)

テスト

では、実際にブラウザでアクセスしてみましょう。
ブラウザウィンドウを2つ開き、「http://localhost/bin/SampleChat.html」にアクセスします。 Browser_preview_1
【画面】 複数ブラウザウィンドウの表示
FlexBuilderで作成したウィンドウが正しく表示されれば、Railsサーバーは正しく稼働しています。 Browser_preview_2
【画面】 チャット内容の入力
「送信」ボタンを押すと、入力内容がdangoサーバーに送信されます。
dangoサーバーは、送信された文字を接続中の全てのクライアントに対して配信しますので、 チャットが正しく実装されていれば、送信した文字が、両方のウィンドウで表示されるはずです。 Browser_preview_3
【画面】 サーバーからのメッセージ受信

補足情報:デバッグについて

dangoサーバーのデバッグ方法
dangoサーバーのデバッグには、RAILS_ROOT/log/dango_{RAILS_ENV}.logへのログ出力を使用します。
ログレベルの設定は、/config/dango/{RAILS_ENV}.rbで行っています。
サーバープログラム上でlogger.debug()やlogger.info()を用いて出力した情報は全て、上記ログファイルに出力されます。
Loggerについての詳しい情報は、標準添付ライブラリ紹介 【第 2 回】 Loggerなどをご覧ください。
Flash(Flex)のデバッグ方法
Flashのログを出力させるには、Flash Debug Player (FDP)をインストールする必要があります。
FDPのインストール方法については、以下URLをご覧ください。
Railsサーバーのデバッグ方法
Railsサーバーのデバッグには、RAILS_ROOT/log/{RAILS_ENV}.logへのログ出力を使用します。
サーバープログラム上でlogger.debug()やlogger.info()を用いて出力した情報は全て、上記ログファイルに出力されます。
Loggerについての詳しい情報は、標準添付ライブラリ紹介 【第 2 回】 Loggerなどをご覧ください。