— boreal-kiss.com

Archive
Tag "Rails"

どちらも同じ出版社の本だけど対照的。

  • Adobe Air in Action
    Adobe Air in Action

    AIRの良書。Flash, Flexに慣れ親しんだ人がAIRについてのみ知りたい場合などに最適。逆にFlashの知識が全くない状態でAIRをやってみようという場合には解読不能かも。Window, File system, Drag-and drop, SQLiteなど、AIR特有の機能に内容を絞っていて、しかもかなり原理的なところから説明しているので好感度が高い(FileStreamの進行状況を視覚化してあったり)。難点を挙げるとすると、これは出版社MANNINGの問題なんだけど、ここの本のレイアウト、文字スタイルが無駄に凝ってたり見出しごとにフォントかえてたりして見づらいんだよね。目がチカチカするっていうか。O’Reillyはそのへんさすがだと思う。

    » レーダーチャート評点

  • Flexible Rails: Flex 3 on Rails 2
    Flexible Rails: Flex 3 on Rails 2

    Flex + Rails。2008年に購入した本の中でぶっちぎり独走状態のクソ本。扱っている内容はFlex, Railsとも実践的で高度なのだが(Flex 3, Rails 2.0, RubyAMF, Cairngormなどを一緒に扱っている本はたぶん世の中にこれしかない)、著者の文章力がお粗末。自分の挙げた質問に脱線しまくったあげく見当違いな答えが書いてあったりして愕然とさせられる。要所要所でかいま見られる「この本への著者の愛」も読んでて非常に気持ち悪い。「TVゲームが超絶うまい小学生に書かかせたゲーム攻略本の文章」っていうとイメージ湧くかな。この本のせいで出版社のMANNINGのイメージがよろしくなくなった。

    » レーダーチャート評点

Read More

Railsからの応答をE4Xで受信した場合とAMFで受信した場合を比較した。使ったのはidとnameからなる1000行のMySQLデータ。Flexからリクエストしてから応答までの時間を測定したところ、AMFの方が5倍も遅くなって困惑した。環境はFlex SDK 3.0, Ruby 1.8.6, Rails 2.1, MySQL 5.0.45で全て同じローカルマシン内。

Rails側

RubyAMFをインストールしたRailsプロジェクト内にUserモデルとUsersControllerを作成。UserモデルはMySQLのusersテーブル(id, name)とシンクロ済み(テストデータはgeneratedata.comで作成)。UsersControllerに以下のようにindexを記述。

class UsersController < ApplicationController

  def index
    @users = User.find(:all)    
    respond_to do |format|
      format.xml {render :xml => @users} # For HTTPService
      format.amf {render :amf => @users} # For RemoteObject
    end
  end
end

AMFの結果をArrayCollectionで簡単に受け取れるようrubyamf_config.rbを一部修正。ただしこの設定はRemoteObjectに対して有効で、NetConnectionではArrayCollectionが使えないため無効。

require 'app/configuration'
module RubyAMF
  module Configuration
    ClassMappings.assume_types = true
    ClassMappings.use_array_collection = true
  end
end

Flex側 (HTTPService – E4X)

load()とonResult()までの時間を10回測った。測定時間はTextAreaに、受信内容はDataGridにそれぞれ表示。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">

    <mx:HBox width="100%" height="100%">
        <mx:DataGrid id="dg" width="50%" height="100%">
            <mx:columns>
                <mx:DataGridColumn dataField="id"/>
                <mx:DataGridColumn dataField="name"/>
            </mx:columns>
        </mx:DataGrid>
        <mx:TextArea id="loadText" width="50%" height="100%"/>
    </mx:HBox>  
    <mx:Button label="Load" width="100" height="30" click="load(event)" />

    <mx:Script>
        <![CDATA[
            import mx.rpc.http.HTTPService;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.events.FaultEvent;
            import flash.utils.getTimer;
            
            private var _start:uint;
            private var _str:String = "";
            
            private function load(e:Event):void{
                var req:HTTPService = new HTTPService();
                req.url = "http://127.0.0.1:3000/users.xml";
                req.resultFormat = HTTPService.RESULT_FORMAT_E4X;
                req.send();
                req.addEventListener(ResultEvent.RESULT,onResult);
                req.addEventListener(FaultEvent.FAULT,onFault);
                _start = getTimer();    
            }
            
            private function onResult(e:ResultEvent):void{
                dg.dataProvider = e.target.lastResult.user;
                _str += (getTimer() - _start).toString() + " msec\n";
                loadText.text = _str;
            }
            
            private function onFault(e:FaultEvent):void{
                loadText.text = e.fault.faultDetail;
            }
        ]]>
    </mx:Script>
</mx:Application>

Flex側 (RemoteObject – AMF)

MXMLの骨格はHTTPServiceを使う場合と同じ。スクリプト部分とservices-config.xmlの設定は以下の通り。

スクリプト部分

<mx:Script>
    <![CDATA[
        import mx.rpc.remoting.RemoteObject;
        import mx.rpc.events.ResultEvent;
        import mx.rpc.events.FaultEvent;
        import flash.utils.getTimer;
        
        private var _start:uint;
        private var _str:String = "";
        
        private function load(e:Event):void{
            var ro:RemoteObject = new RemoteObject();
            ro.source = "UsersController";
            ro.destination = "rubyamf";
            ro.index();
            ro.addEventListener(ResultEvent.RESULT,onResult);
            ro.addEventListener(FaultEvent.FAULT,onFault);
            _start = getTimer();
        }
        
        private function onResult(e:ResultEvent):void{
            dg.dataProvider = e.result;
            _str += (getTimer() - _start).toString() + " msec\n";
            loadText.text = _str;
        }
        
        private function onFault(e:FaultEvent):void{
            loadText.text = e.fault.faultString;
        }
    ]]>
</mx:Script>

services-config.xml (コンパイラオプションに追加)

<?xml version="1.0" encoding="UTF-8"?>
<services-config>
    <services>
        <service id="remoting-service" 
            class="flex.messaging.services.RemotingService" 
            messageTypes="flex.messaging.messages.RemotingMessage">
            <destination id="rubyamf">
                <channels>
                    <channel ref="ch-rubyamf" />
                </channels>
                <properties>
                    <source>*</source>
                </properties>
            </destination>
        </service>
    </services>
    
    <channels>
        <channel-definition id="ch-rubyamf" 
            class="mx.messaging.channels.AMFChannel">
            <endpoint uri="http://127.0.0.1:3000/rubyamf/gateway" 
                class="flex.messaging.endpoints.AMFEndpoint" />
        </channel-definition>
    </channels>
</services-config>

結果

HTTPService – E4X

load()からonResult()までの時間測定は右テキストエリア内(msec)。10回測定でだいたい0.5-0.6秒。ちなみにRESULT_FORMAT_OBJECT(ActionScript Object)で受信すると0.7-0.8秒ぐらいだった。

RemoteObject – AMF

HTTPServiceの場合と同じ測定で一回あたり約3秒。

AMF、通信設定が面倒なくせにすげー遅い。Ajax and Flex Data Loading Benchmarks | James Ward – RIA Cowboyの結果と違いすぎる。どっか間違ってる?

Read More

例えばデータベースにfirst_name, last_nameというコラムを持つusersというテーブルがあって、Railsでデータベースの内容をto_xmlで呼び出すと、以下のようにfirst_nameとlast_nameのアンダースコアを勝手にハイフンに変える。

<?xml version="1.0" encoding="UTF-8"?>
<users>
    <user>
        <first-name>Colin</first-name>
        <last-name>Moock</last-name>
    </user>
</users>

Flexでこのxmlデータのノードを受け取る場合

//Data set from Rails
var user:XML;

trace(user.child("first-name"));//Colin

とするか、Rails側で勝手にハイフンに変えないようにする。

@user = User.find(params[:id])
render :xml => @user.to_xml (:dasherize => false)
Read More

注意: 書かないと確実に忘れてミスりそうなのでメモ。当方SVN初心者。もっと手順の少ない楽な方法があれば是非教えてください。

やりたいこと

ローカルのRailsアプリケーションの変更をリモートサーバー上のRailsアプリケーションに「安全に」反映させたい。

Railsの性質上、ローカル – リモートサーバー間の直接の書き換えは超絶危険なので、中継地点としてSVNリポジトリを利用する。これにより、ローカルの変更はSVNリポジトリへ別(サブ)バージョンとして保管、実際のリモートサーバー上での運営はSVNリポジトリから適切なバージョンを拾ってくることで反映させる。つまり手元で変ちくりんな修正を行ってしまっても古いバージョンが保管されているのでリセット可能。かなり大胆なweb運営ができる。

簡単な環境説明

RailsPlayground.comは名前の通り、Ruby on Railsアプリケーションが簡単に使えるのが売りの海外のレンタルサーバー。例えばさくらインターネットだとRailsは手動でインストールできるけど、FastCGIが使えないので動作が遅いらしい(例えばがんちゃんのブログ: さくらのレンタルサーバでRuby on Railsをうごかしてみた…らかなり遅いかも…)。日本国内だとRailsが標準装備のレンタルサーバーは値段がはってしまうようなので、月額$5のRailsPlaygroundを借りてみた。海外だと他にもBluehostTextdrive(現Joyent?)等が有名みたいだけど少し高い。

RailsPlayGroundの$5プランは破格なんだけど使用できるサーバーがApacheオンリーのようなので、Lighttpd等を複数使いたい場合は上のプランを選ばないといけない。あとRailsPlayGroundは契約フォームまわりがすっごくうさんくさかった。User IDが6文字までとか、いきなりクレジットカード番号いれさせられたり、なんか諸々。性能やら手続きやらについてはRailsPlaygroundの申し込み方法 (Ruby on Rails)あたりがわかりやすいかも。サブドメイン、データベースが作成し放題なので、例えばxreaみたいにスパムサイトの温床になりそうなので少し気がかりだけど、今のところ快適に動作している。

SVNリポジトリ設定

  1. RailsPlaygroundにSVNで管理したい旨をメールで要請(要請しないと使えない)。24時間サポートらしくすぐ専用のURLが送られてくる。アクセス先(IDとPW入力)でリポジトリスペースとアカウント作成などを行う。ここではリポジトリ名をrailsappとしておく。SVNリポジトリの保管先はこんな感じになる。
    http://YOURACCOUNT.svnrepository.com/svn/railsapp
    

    YOURACCOUNT部分はサーバー管理者が適当に与えてくれる。

  2. RailsPlaygroundサーバースペースにRailsアプリケーション(以下railsapp)を作成(ドキュメントルートより上)。理由はrailsappを作成する際、Apacheで動作するよう.htaccessをRailsが勝手に作成してくれるから。Apacheでちゃんと動作するように.htaccessを自分で作成できるならこの手順は不要。Apacheサーバーで運営する予定のない人にも不要。
  3. 先ほど作成したrailsappプロジェクト全部をローカルに保存。実際に欲しいのは.htaccessファイル「のみ」なので、事前にローカルで作成したRailsアプリケーション内に.htaccessだけコピーしてもってきてもよいと思う。
  4. ローカルのrailsappプロジェクト全部をSVNリポジトリにインポート。ローカルのrailsappディレクトリ内より
    svn import -m "MESSAGE" . http://YOURACCOUNT.svnrepository.com/svn/railsapp
    

    MESSAGE部分はお好みで(“First import”など)。

  5. インポートが完了したら手元にSVNリポジトリからのコピーを持ってこないといけない(リポジトリとシンクロさせるため)。とりあえずローカルのrailsappを別ディレクトリに移すなどする(ここではrailsappディレクトリと同じ階層にrails_backとして保存)。
    mv /path/to/railsapp /path/to/railsapp_back
    
  6. あらためてリポジトリよりチェックアウト。コピーしたい場所より
    svn co http://YOURACCOUNT.svnrepository.com/svn/railsapp
    

    これでSVNリポジトリrailsappとローカルrailsappがシンクロする。ローカルrailsappを変更してSVNリポジトリへコミット(ci)すると別バージョンとして保存されるはず。

  7. 次にSVNリポジトリのrailsappをRailsPlaygroundのリモートサーバー上へコピーする。これも基本はローカル – SVNリポジトリの場合と同じように、SVNリポジトリ – リモートサーバー間でシンクロさせてやる必要がある。RailsPlaygroundの自分のドメインへアクセス、sshで
    ssh YOURID@YOURDOMAIN.COM
    

    YOURIDとYOURDOMAIN.COMはRailsPlaygroundの契約IDと設定ドメイン。

  8. ドキュメントルートより上にSVNリポジトリをチェックアウト。手順2ですでにrailsappを作成している場合は全部削除。かわりにSVNリポジトリより呼び出す。
    svn co http://YOURACCOUNT.svnrepository.com/svn/railsapp
    

    これでリモートサーバーとSVNリポジトリがシンクロする。必要なときに必要なバージョンをSVNリポジトリから拾ってくればよい。バージョンの選び方等はSubversionの使い方を参照ください。

  9. RailsPlaygroundサーバー上railsapp/publicのシンボリックリンクをドキュメントルート配下に設置。例えば
    ln -s ~/railsapp/public ~/public_html/railsapp
    

    これでhttp://YOURDOMAIN.COM/railsappにアクセスするとSVNリポジトリの内容を反映したRailsアプリケーションが表示されるはず。Railsアプリケーションをドキュメントルート以下に配置すると色々不都合があるためこのような回りくどい処理を行う。

基本の作業手順

ローカル – SVNリポジトリ、SVNリポジトリ – リモートサーバーがシンクロしていることが前提。

  • SVNリポジトリより必要なバージョンをチェックアウト
    svn co http://YOURACCOUNT.svnrepository.com/svn/railsapp
    
  • ローカル作業後、SVNリポジトリへコミット。メッセージはお好みで。
    svn ci -m "MESSAGE" http://YOURACCOUNT.svnrepository.com/svn/railsapp
    
  • RailsPlaygroundサーバーへアクセス
    ssh YOURID@YOURDOMAIN.COM
    
  • SVNリポジトリよりリモートサーバーへチェックアウト
    svn co http://YOURACCOUNT.svnrepository.com/svn/railsapp
    
  • web上に反映される。
  • Read More

    データベースに格納された本のスコアデータをxmlに整形してFlexチャートに読み込ませたい。実際のデモはBook reviewを参照ください。デモの場合、各ページごとにFlexに読み込ませたいデータが異なるのでFlashVarsを用いてswfファイル外部からコントロールしてやる。

    Radar chart

    Rails側

    スコアデータ5種類用意。特定のbook idのスコアデータをFlexで読み込みやすいようにxmlデータで出力させる。Flex側で極力整形を避けたかったので、Rails側で以下のようなxmlデータを作るURL(例えば、:controller => ‘book’, :action => ‘create_xml’, :id => @book.id)を作成しておく。具体例はFlex + Ruby on Rails + MySQL 連携メモを参照ください。

    <?xml version="1.0" encoding="UTF-8"?>
    <grades>
        <grade>
            <label>Easy to read?</label>
            <score>9</score>
        </grade>
        <grade>
            <label>Good contents?</label>
            <score>8</score>
        </grade>
        <grade>
            <label>Latest version?</label>
            <score>7</score>
        </grade>
        <grade>
            <label>Reasonable price?</label>
            <score>10</score>
        </grade>
        <grade>
            <label>Recommend to people?</label>
            <score>6</score>
        </grade>
    </grades>
    

    各本詳細ページ上にswfファイルを埋め込み、その本のスコアデータXMLを出力するURLをFlashVarsで指定してやる(最後の一行部分)。

    AC_FL_RunContent(
        "src", "path/to/swf_file",
        "width", "300",
        "height", "220",
        "align", "middle",
        "id", "swf_id",
        "quality", "high",
        "bgcolor", "#ffffff",
        "name", "swf_name",
        "allowScriptAccess","sameDomain",
        "type", "application/x-shockwave-flash",
        "pluginspage", "http://www.adobe.com/go/getflashplayer",
        "FlashVars", "url=path/to/<%= @book.id %>"
    );
    

    FlashVarsに送るURL部分を

    "Flashvars", "url=<%= url for :controller => 'book', 
                    :action => 'create_xml', :id => @book.id %>"
    

    のように書いてもよいが、Apacheの場合、url_forの出力がRailsアプリケーションディレクトリ(railsapp)からのパスになるので注意が必要。例えば@book.id=5だと

    //Apacheの場合
    /railsapp/book/create_xml/5
    
    //Lighttpdの場合
    /book/create_xml/5
    

    となる。この辺の話は [Rails] Rails の動作環境と Location について – プログラミングは素晴らしいが詳しい。

    Flex側

    FlashVarsで送信された変数を受け取る。Flash(ActionScriptクラス)の場合と異なるので注意必要。

    //Flex Applicationの場合
    var params:Object = Application.application.parameters;
    trace(params.url);//"path/to/xml_data"
    
    //Flashの場合
    var params:Object = this.loaderInfo.parameters;
    trace(params.url);//"path/to/xml_data"
    

    Flex内部のArrayCollectionに格納するまでの流れは以下の通り(抜粋)。完全なソースはこちらを参照ください。

    import mx.rpc.http.HTTPService;
    import mx.rpc.events.ResultEvent;
    import mx.collections.ArrayCollection;
    
    var params:Object = Application.application.parameters;      
    var req:HTTPService = new HTTPService();
    req.url = params.url;
    req.send();
    req.addEventListener(ResultEvent.RESULT, onResult);
                
    function onResult(e:ResultEvent):void{
        var ac:ArrayCollection = e.result.grades.grade;
        //データ処理に続く
    }
    

    追記: 2008/08/08

    チャート部分をFlexからActionScriptのみで作成したところ、ファイルサイズが236kb -> 4kbになった。

    Read More

    ブログでレビューを書いてもいいのだけど、せっかくだからRailsを使ってレビューサイトを作成した。レビュー書いたりかかなかったりだけど、レーダーチャート(?)で何となく予想してみてください。可能ならFlashなりなんなりの有志にアカウント与えて技術書の各々のレビューを残したりできるとよいかもしれない。

    boreal-kiss.net

    Read More

    データベースの情報をRailsでXMLファイルに整形しFlexに読み込ませる。Model-View-Controller(MVC)パターンで言うとMCをRailsに、VをFlexに担当させる。今までデータベースへのアクセスはPHPスクリプトを書いて行っていたが、Railsを用いるとその面倒くさいテンプレート書きが必要なくなるので非常に快適。用いたのはFlexSDK 3.0, Ruby 1.8.6, Rails 1.2.6, MySQL 5.0.5。Railsは最新の2.xを用いても特に問題ないと思う。以下の例ではデータベースに格納されたの本の詳細(title, price, description)をRailsで管理、FlexのGridDataに読み込ませている。

    Rails側

    Railsで適当なプロジェクト(ここではflex_connect)を作成。コマンドラインより

    rails flex_connect
    

    データベースを作成。MySQLコマンドラインより

    mysql> create database flex_connect_development;
    

    Railsプロジェクト内config/database.ymlを設定(今回はdevelopmentのみ使用)。

    development:
      adapter: mysql
      database: flex_connect_development
      username: root
      password: [YOURPASSWORD]
      socket: /tmp/mysql.sock
    

    次にモデルを作成(ここではBook)。Railsプロジェクト内に移動してコマンドラインより

    ruby script/generate model Book
    

    Railsプロジェクト内、db/migrate/xxx_create_books.rb(xxxの部分はrailsのバージョンによって異なる)を以下のように修正。

    class CreateBooks < ActiveRecord::Migration
      def self.up
        create_table :books do |t|
          t.column :title, :string
          t.column :price, :float
          t.column :description, :text
        end
      end
    
      def self.down
        drop_table :books
      end
    end
    

    コマンドラインより

    rake migrate
    

    これによりDB: flex_connect_developmentにテーブルbooks(title, price, description)が作成される(MySQLを直接触らなくてよい!)。テーブル内に適当にテストデータを入力しておく(title=”Flex3″, price=”54.99″, description=”Adobe developer library”, etc.)。Railsから入力してやるのが簡単だがここでは割愛。

    次にコントローラーを作成。コマンドラインより

    ruby script/generate controller Book
    

    Railsプロジェクト内、app/controllers/book_controller.rbに以下のメソッドを定義。

    class BookController < ApplicationController
      def make_xml
        @books = Book.find(:all)
        render :xml => @books.to_xml
      end
    end
    

    これで”http://localhost:3000/book/make_xml”にアクセスするとデータベース情報がXML形式で出力される。例えばこんな感じ。

    <?xml version="1.0" encoding="UTF-8"?>
    <books>
      <book>
        <description>Ruby on Rails made easy.</description>
        <id type="integer">3</id>
        <price type="float">34.99</price>
        <title>Rails Solutions</title>
      </book>
      <book>
        <description>The Adobe developer library guide for rich interner application developers</description>
        <id type="integer">4</id>
        <price type="float">44.99</price>
        <title>Flex 3 Cookbook</title>
      </book>
      <book>
        <description>Friends of ED Adobe lerning library.</description>
        <id type="integer">5</id>
        <price type="float">54.99</price>
        <title>The Essential Guide To Flex 3</title>
      </book>
    </books>
    

    Flex側

    “http://localhost:3000/book/make_xml”の内容をDataGridに表示させるソースは以下の通り。上記RailsのXMLファイルを出力するURLをロードしてDataGridに格納している。詳細は割愛。

    <?xml version="1.0" encoding="utf-8"?>
    <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
                        layout="absolute" creationComplete="init(event)">
        
        <mx:DataGrid dataProvider="{_bookList}">
            <mx:columns>
                <mx:DataGridColumn dataField="title" />
                <mx:DataGridColumn dataField="price" />
                <mx:DataGridColumn dataField="description" />
            </mx:columns>
        </mx:DataGrid>
    
        <mx:Script>
            <![CDATA[
                import mx.collections.ArrayCollection;
                import mx.rpc.http.HTTPService;
                import mx.rpc.events.ResultEvent;
                import mx.rpc.events.FaultEvent;
                import mx.controls.Alert;
                import flash.net.URLRequestMethod;
                
                [Bindable]
                private var _bookList:ArrayCollection
                
                private function init(e:Event):void{
                    var req:HTTPService = new HTTPService();
                    req.url = "http://localhost:3000/book/make_xml";
                    req.method = URLRequestMethod.POST;
                    req.send();
                    req.addEventListener(ResultEvent.RESULT,onResult);
                    req.addEventListener(FaultEvent.FAULT,onFault);
                }
                
                private function onResult(e:ResultEvent):void{
                    _bookList = e.result.books.book;
                    e.target.removeEventListener(ResultEvent.RESULT,onResult);
                    e.target.removeEventListener(FaultEvent.FAULT,onFault);   
                }
                
                private function onFault(e:FaultEvent):void{
                    Alert.show("Could not connect the database.","ERROR");
                    e.target.removeEventListener(ResultEvent.RESULT,onResult);
                    e.target.removeEventListener(FaultEvent.FAULT,onFault);   
                }
            ]]>
        </mx:Script>
    </mx:Application>
    

    まとめ

    今回はRails側にMVCパターンのMCを担当させた。同様にしてRails側のコントローラーに適切なメソッド(データの追加・削除 etc.)を追加することで専用のURLを簡単に作成できるので、Flex側は必要なURLにアクセスするだけでよくなった。

    Read More

    Ruby on Rails: Up And Running (Up and Running)

    結論: Railsのバージョンの違いは致命的。この本は扱っているRailsのバージョンが1.1と古いので買うべからず。買うならRails 2.xと明記した最近の本を。

    O’reillyでRuby on Railsを扱っている本がこれしかなかったので盲目的に買ったが失敗。扱ってるRailsのバージョンが古すぎて(1.x)、現行のRails 2.xだと四苦八苦することが多い。本自体は150ページと割と薄めで、一つのRailsアプリケーションを題材に進行していくため、(頭に入るかどうかは別として)よみすすめやすくはある。ただしRuby自体の教本ではないので(それほど親切に説明はない)Rubyのリファレンスは必要。僕は平行してThe Ruby Programming Languageを読んだ。

    Railsのバージョンアップは初学者にはかなりの障壁になるようなので、もし手持ちの本がRails 1.xを扱っているようであれば、Railsのバージョンを落として学ぶか(仕組みを覚えてから2.xに移行)、2.xを扱う本を買った方が効率がよいと思う。以下Rails 1.x > 2.xの違いについて参考にしたページ。

    追記 2008/08/08

    レーダーチャートによる評価レビューはこちら

    Read More