22

在Rails中使用Flash Remoting中文教程

分类:default | | 给我留言

原文:Flash Remoting for Rails Tutorial

里克:1、Flash Remoting从flash player6就开始了,Flex Client是个新家伙。2、Flash Remoting如何翻译?我想翻译为flash远程。

在我们使用Rails的时候,可能会忽视掉另一个开发工具:Flash Remoting。长期的使用xml,可能会改变你的编程习惯。就像作者引用的那句话:”If all you have is a hammer everything looks like a nail”。Remoting可以直接向flash传递对象,数组。Remoting使用AMF(Active Message Format)传递byte流,这比使用xml要快很多。

而且在06年8月,Midnight Coders发布了他们的WebORB插件。

他们的例子是基于Flex2的,这需要客户端安装Flash player9.0,这并不有好,因为Remoting在Flash player6中就已经开始使用了。所以这里我们只拿我们需要的。

如果你之前没有使用过Flash Remoting,请先安装Remoting Components

这里我们制作一个flash的mp3播放器,用rails作为后端服务。点击这里,你可以下载这个例子的代码。

创建一个应用:mp3app

> rails mp3app
> cd mp3app

安装weborb插件

> ruby script/plugin install http://themidnightcoders.net:8089/svn/weborb

创建一个名为‘mp3app_development’的数据库

CREATE TABLE `tracks` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(50) NOT NULL default '',
  `artist` varchar(50) NOT NULL default '',
  `album_art` varchar(50) NOT NULL default '',
  `filename` varchar(50) NOT NULL default '',
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

insert into `tracks` values
('1','After Midnight','Eric Clapton','clapton.jpg','AfterMidnight.mp3'),
 ('2','Midnight Train to Georgia','Gladys Knight','gladys.jpg',
'MidnightTrainToGeorgia.mp3'),
 ('3','Midnight In A Perfect World','DJ Shadow','shadow.jpg',
'MidnightInAPerfectWorld.mp3'),
 ('4','Two Minutes to Midnight','Iron Maiden','maiden.jpg',
'TwoMinutesToMidnight.mp3');

创建‘Track’model

> ruby script/generate model Track

在‘app/services’目录下创建一个Remoting services服务,文件为:’TrackService.rb’

require 'weborb/context'
require 'rbconfig'

class TrackService
   def getTracks
     tracks = Track.find(:all)
   end
end

如果你曾经用过AMFPHP或者.net下的Remoting,上面的这个服务端可以说是“相当”简单了。但是你可以运行测试一下。

下面进入到我们的Flash。
对于Flash应用,有几个框架可供选择,比如ARPCairngorn。但是在我们这个应用上,他们显得太过强大了。我们可以写一个自己的轻量级的框架。这是一种MVC框架,Model是一个Remoting Class,用来和后台程序沟通(PHP, .NET, 或者 我们的 Rails) ,一个.fla的controller,和一个View class。
M 和 V 在 C 的控制下进行事件相应与处理,而其他的类来扩展其他的功能。

基本的Remoting是这样:

import mx.remoting.Service;
import mx.services.Log;
import mx.remoting.PendingCall;;
import mx.rpc.RelayResponder;
import mx.rpc.FaultEvent;
import mx.rpc.ResultEvent;
import mx.remoting.debug.NetDebug;
import mx.utils.Delegate;

class com.vixiom.remoting.Remoting
{
    private var gatewayURL:String;
    private var servicePath:String;

    private var svc:Service;

    function dispatchEvent() {};
    function addEventListener() {};
    function removeEventListener() {};

    /**
    * Constructor
    *
    *   @param      gURL    gatewayURL
    *   @param      sp      service path
    *   @param      u       username
    *   @param      p       password
    *
    */
    public function Remoting(gURL, sp, u, p)
    {
        gatewayURL = gURL;
        servicePath = sp;

        // initialize as a broadcaster
        mx.events.EventDispatcher.initialize(this);

        // create a new service
        svc = new Service (gatewayURL, null, servicePath, null, null);

        // credentials
        if (u != undefined && p != undefined) {
            svc.connection.setCredentials(u, p);
        }

    }

    /**
    * Global fault event
    */
    function handleRemotingError(fault:FaultEvent):Void
    {
        mx.remoting.debug.NetDebug.trace({level:"None",
        message:"Error: " +
        fault.fault.faultstring });
    }

    /**
    * Event dispatcher
    *
    *   @param      d       data
    *   @param      et      eventType
    *
    */
    function dispatch(d, et)
    {
        // broadcast message
        var eventObj:Object={target:this,type:et}
        eventObj.data = d;
        dispatchEvent(eventObj);
    }

}

基本的View Class是这样:

import mx.utils.Delegate;

class com.vixiom.view.View
{
    private var target:MovieClip;

    function dispatchEvent() {};
    function addEventListener() {};
    function removeEventListener() {};

    /**
    * Constructor
    *
    *   @param  t   target (target timeline: _root || a mc)
    */
    public function View (t:MovieClip)
    {
        target = t;

        // initialize as a broadcaster
        mx.events.EventDispatcher.initialize(this);
    }

    /**
    * Event dispatcher
    *
    *   @param      d       data
    *   @param      et      eventType
    *
    */
    function dispatch(d, et)
    {
        // broadcast message
        var eventObj:Object={target:this,type:et}
        eventObj.data = d;
        dispatchEvent(eventObj);
    }
}

里克:看到这个时候,我看了一下源代码。as文件的存放是按照类似java的形式。所以下面的话就好理解了。我对as的知识为0.

在这个MP3 player中,我用下面的方法扩充一下Remoting class。你可以不必把你的类放到这个packages里面,当然那样做更利于管理。

'var pc:PendingCall = this.svc.getTracks();'

一句是再远程调用Rails,这和TrackService.rb 类里面的’getTracks’是一样的。我的类中有一个tracks对象,它直接接收ruby的’tracks = re.result;’。我注释了’this.svc.connection.setCredentials(u, p);’一行,因为它在创建一个安全的远程调用。最后一行’dispatch(tracks, “onGetTracks”);’ 将数据传递给view。

import mx.remoting.*;
import mx.rpc.*;

class com.vixiom.remoting.RemotingMp3
extends com.vixiom.remoting.Remoting
{
    // tracks holder object
    private var tracks:Object;

    ////////////////////////////////////////////////////////////////
    //
    // Constructor (gatewayURL, servicePath, userid, password)
    //
    ////////////////////////////////////////////////////////////////

    public function RemotingMp3 (gURL, sp, u, p)
    {
        super(gURL, sp);
        // this.svc.connection.setCredentials(u, p);
    }

    ////////////////////////////////////////////////////////////////
    //
    // Get tracks with handler (onGetTracks)
    //
    ////////////////////////////////////////////////////////////////

    public function getTracks()
    {
        trace("// getting tracks")
        // create a pending call out to rails
        var pc:PendingCall = this.svc.getTracks();
        // create a responder to handle the return from rails
        pc.responder = new RelayResponder(this,
        "onGetTracks",
        "handleRemotingError");
    }

    public function onGetTracks (re:ResultEvent)
    {
        if (re != undefined)
        {
            trace("// onGetTracks broadcaster - Word!")
            // put result in recordset
            tracks = re.result;
            // trace for testing
            for (var i = 0; i < tracks.length; i++) {
                trace(tracks[i].title);
            }
            // dispatch event to the view
            dispatch(tracks, "onGetTracks");
        }
    }
}

下面是controller的代码,需要注意的是,‘Rmp3’是我扩展的Remoting class的实例。它有两个参数,一个是WebORB geteway url,一个是在app/services中的’TrackService’类。我还没有展示扩展的view class,controller里已经产生了一个‘Vmp3’实例,with the _root of the Flash file as it’s parameter (it uses that as a target). 。下面四行是view中的按钮方法,最后一行调用远程方法,这是这个app的entry point(as it’s pretty useless sans data)

// import remoting, view, and debug
import mx.remoting.debug.NetDebug;
import mx.utils.Delegate;
import com.vixiom.remoting.RemotingMp3;
import com.vixiom.view.ViewMp3;

// ini debug
NetDebug.initialize ();

iniApp();

// setup and start
function iniApp()
{
    // create remoting & view objects
    var Rmp3:RemotingMp3    = new RemotingMp3 (
    "http://localhost:3000/weborb",
    "TrackService"); // weborb gateway, ruby class name
    var Vmp3:ViewMp3        = new ViewMp3 (_root);

    // set up listeners
    Rmp3.addEventListener ("onGetTracks",
    Delegate.create (Vmp3, Vmp3.onGetTracks));

    pause_btn.onRelease = Delegate.create(Vmp3, Vmp3.pauseTrack);
    play_btn.onRelease  = Delegate.create(Vmp3, Vmp3.playTrack);
    prev_btn.onRelease  = Delegate.create(Vmp3, Vmp3.previousTrack);
    next_btn.onRelease  = Delegate.create(Vmp3, Vmp3.nextTrack);

    // start the app, get the tracks
    Rmp3.getTracks();
}

下面是扩展的view,相当复杂,而且和rails及Remoting没什么关系,所以看一下它的注释,就能明白它的意思了。里克:我就不贴了,看类名就知道它在哪了。

class com.vixiom.view.ViewMp3 extends com.vixiom.view.View

就是这样了,不过要注意一下跨域安全问题,尤其是当你的rails应用在一台服务器上,而Flash文件在另一台服务器上的时候。

里克,07年10月23日,Railser.cn

  • 标签 :  , ,
  • 原文链接 : http://railser.cn/index.php/blog/flash-remoting-for-rails-tutorial-chinese
  • 转载原创文章请注明 : 里克的自习室
  • 收藏到 : Google书签 新浪ViVi 365Key网摘 天极网摘 我摘 POCO网摘 博采网摘 YouNote网摘 和讯网摘 博拉网 igooi网摘 I2Key网摘 天下图摘 百特门网摘 Del.icio.us Yahoo书签 奇贴 QQ娱乐摘 添加到Digg! 添加到Facebook!
  • 评论已经关闭...