※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

【参考文献】:AJAX HACKS 6章 Hacks45-52(p207~)

MVCアーキテクチャ

MVCとはデザインパターンの一種で、アプリケーションの構成を、Model、View、Controllerというコンポーネントに分けて設計する手法を言う。

Ruby On Rails のインストール

編集中。。。あまりここに時間を割きたくないな。ぼそ

ここでは、InstantRails-1.7-win.zipを導入して使います。

 


Rails アプリの作成

1.開発環境

始める前に、念のため開発に使用するマシンのスペックを調べておきます。

Windows XP SP2 であれば、まあ問題ないでしょう。今回はWindows2000でやってみます。

ちなみに、マシンスペックの調べ方は、コマンドで「dxdiag

C:\InstantRails\rails_apps>dxdiag

今回の開発環境のサンプル)

------------------
System Information
------------------
Time of this report: 10/16/2008, 09:52:20
Machine name: HIDE2000
Operating System: Windows 2000 Professional (5.0, Build 2195) Service Pack 4
Language: Japanese (Regional Setting: Japanese)
System Manufacturer: VMware, Inc.
System Model: VMware Virtual Platform
BIOS: Default System BIOS
Processor: Intel(R) Core(TM)2 Quad CPU    Q6600  @ 2.40GHz, ~2.4GHz
Memory: 724MB RAM
Page File: 408MB used, 1049MB available
Windows Dir: C:\WINNT
DirectX Version: DirectX 9.0c (4.09.0000.0904)
DX Setup Parameters: Not found
DxDiag Version: 5.03.0001.0904 32bit Unicode

【補足:開発支援の為のIDE Tool】

開発支援の為のIDEToolとして、eclipseAptanaNetBeansなどを利用すると便利だと思います。

今回は「eclipse」に「Aptana」プラグイン、「RadRails」プラグインを仕込んで使います。(

※)手っ取り早く環境を構築するには?

これらが全部そろった「pleiades-all-in-one-ultimate_20081006.zip」がお勧めです。(=>ダウンロードはこちらから。Eclipse 3.4.1 Ganymedeのultimateがお勧めです。ちなみに3.3.1のほうにはultimateでも今回使いたいRadRailsのプラグインが入ってませんでした。。。)

※) eclipse 3.2.1 に RadRailsプラグインを追加する方法


AptanaプラグインとRailsプラグインのインストール
Eclipseを起動し、「ヘルプ」→「ソフトウェアの更新」→「検索してインストール」を選びます。
ダイアログボックスが出たら「新規フィーチャーを探す」を選びます。
アップデートサイト選択画面で「新規リモート」を選びます。
ここでは次の2つのサイトを登録します。
http://update.aptana.com/install/3.2/
http://update.aptana.com/install/rails/3.2/
1つ目がAptana本体、2つ目がAptanaのRailsプラグインになります。

リモートサイトを2つ登録し終わったら、「次へ」をクリックし、
以降は通常の更新機能と同様に操作して、インストールを行います。

Railsパースペクティブを開く
プラグインのインストールが終わり、Eclipseを再起動したら、
Railsパースペクティブを開きます。
「ウィンドウ」→「パースペクティブを開く」→「その他」の中に
「Rails」というパースペクティブがあるので、これを選択します。

ここを参考にしました。

http://blog.codezine.jp/editor/2007/06/aptana_eclipseruby_on_rails_1.php

ちなみに、3.3.x だとうまくいきません。3.3用のURLもどこかにあると思いますが、

それは調べていません。

2.Railsのヴァージョン

Railsの開発の場合、特にヴァージョンの組み合わせは重要です。

今回は、InstantRails-1.7-win.zipをインストールして準備した環境なので、「Rails 1.2.3」になります。このヴァージョンが現在最も情報量が豊富です。(=>ダウンロードはこちらから。)

※ rubyの最新の1.9などは1.8.6に比べてだいぶ変わったようですし、フレームワークであるrailsもヴァージョンが1.2.X系2.X系ではscaffoldなどかなりやり方が異なります。パッケージ管理Toolであるgemなども実はgem自体のヴァージョンによりインポートできるモジュールのヴァージョンが違ったりと、手抜かりがあるとなかなか手こずりますので注意しましょう。

C:\InstantRails\rails_apps>ruby -v
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

C:\InstantRails\rails_apps>gem env
RubyGems Environment:
  - VERSION: 0.9.2 (0.9.2)
  - INSTALLATION DIRECTORY: C:/InstantRails/ruby/lib/ruby/gems/1.8
  - GEM PATH:
     - C:/InstantRails/ruby/lib/ruby/gems/1.8
  - REMOTE SOURCES:
     -http://gems.rubyforge.org

C:\InstantRails\rails_apps>rake --version
rake, version 0.7.2

C:\InstantRails\rails_apps>rails --version
Rails 1.2.3

C:\InstantRails\rails_apps>mysql --version
mysql  Ver 14.12 Distrib 5.0.27, for Win32 (ia32)

 

・ もうひとつのヴァージョンの調べ方

ひとつひとつのコマンドを打ち込んでヴァージョンを調べてもいいけれども、Railsの場合たくさんのパッケージやモジュールで構成されていて調べるのはいささか面倒です。そこで一発でこれらを調べるコマンドを紹介します。ただし、これはプロジェクト内部のaboutコマンドなので、少なくとも後述するプロジェクトを何かひとつ作成してから、実行してください。

// これはうまくいきません。

C:\InstantRails\rails_apps>ruby script/about

C:\InstantRails\rails_apps\hacks>ruby script/about
About your application's environment
Ruby version                 1.8.6 (i386-mswin32)
RubyGems version             0.9.2
Rails version                1.2.3

Active Record version        1.15.3
Action Pack version          1.13.3
Action Web Service version   1.2.3
Action Mailer version        1.3.3
Active Support version       1.4.2
Application root             C:/InstantRails/rails_apps/hacks
Environment                  development
Database adapter             mysql

 

3.作成手順

RailsアプリはDB利用を前提としたWebアプリになるため、Railsの命名規則が重要になってきます。ここら辺はDRYCoCの思想によるところですが、今回のAjax用のRailsアプリサンプルとして以下の情報をまず収集します。

  • プロジェクト名:プロジェクトのルートとなる名前、主にフォルダ管理のため。
  • 使用するRDB:MySQL,SQLite3,PostgreSQL,SQL Server,DB2,Oracle
  • DB名:開発用、テスト用、リリース用の三種類を用意します。
  • テーブル名:複数形小文字英数字
  • モデル名:原則としてテーブル名と関連します。単数形のテーブル名

それでは、今回のAjax用のRailsアプリサンプルの情報を定義していきましょう。

  • プロジェクト名:hacks
  • 使用するRDB:mysql
  • DB名:hacks_development,hacks_test,hacks_production
  • テーブル名:kilowatts
  • モデル名:Kilowatt

ここで、一応テーブルの定義も決めておきましょうか。

 

Kilowattsテーブル定義

 NO

カラム名 

 型

 NotNull

 主キー

 1

 id

int 

true 

true 

 2

 kdate

 text

 

 

 3

kwatts 

int 

 

 

DDLファイルを作成するとしたら、以下のようになるかと思います。(

DDL:kilowatts.sql

DROP TABLE IF EXISTS kilowatts;
CREATE TABLE kilowatts (
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    kdate TEXT,
    kwatts INT,
    PRIMARY KEY(id)
);

※)ただし、Railsの作成方法では、このようなDB周りの準備もRailsでサポートされており、具体的にはマイグレーション」という手法を使います。ちなみに、この作成方法はRails 1.2.x 系列とRails 2.x 系列で作成手順が違いますこれは追って説明します。

【参考】:ソースダウンロード

ちなみに、このページはUTF8になります。ブラウザで文字化けなどしたらエンコードを変更してみてください。AjaxHacksSamples.zipを解凍してサンプルコードを手に入れてください。

【事前準備】

InstantRailsでインストールした場合は、InstantRails.exe は起動しておきましょう。起動するとApacheとMySQlが立ち上がります。後ほどRailsからDBを作成したり、テーブルを作成しますが、そのためには今回使用するMySQLがサービスとして起動している必要があります。Webサーバは開発モードでは他の(Mongrelサーバ)を使うのでApacheは立ち上げる必要性はないですが、DBの確認作業で、phpMyAdminというToolを使う場合はApacheサーバを立ち上げておく必要があります。

また、DBにMySQLを使用するのであれば、最初に、my.iniファイルにてDBの文字コード指定をしておきます。以下のソースの青字部分を追記してください。

C:\InstantRails\mysql\my.ini

; ---------------------- IMPORTANT ---------------------------;
; ${path} is used to specify Instant Rails installation path. ;
;-------------------------------------------------------------;

[mysqld]
datadir=${path}/mysql/data
basedir=${path}/mysql
bind-address=127.0.0.1
; Uncomment for use on USB key
; skip-innodb
default-character-set=utf8
skip-character-set-client-handshake

ここまで準備すれば、雛形部分の作成は15分もかからないでしょう

【プロジェクトの作成】

// 1.0 railsアプリのルートフォルダに移動
// InstantRailsでインストールした場合には、その下に「rails_apps」があるはず。そこです。
C:\>cd C:\InstantRails\rails_apps

// 2.0 railsコマンドの実行
// 通常は、Rails 1.2.3 の場合、DBは mysql になります。
// rails hacks
//
// デフォルトじゃないDBシステムを指定するには
// rails hacks -d mysql
// かもしくは、
// rails hacks --database=mysql
//
C:\InstantRails\rails_apps>rails hacks -d mysql

 【DB作成】

DBの作成の仕方には幾通りかあります。各ベンダーにより簡単なやりかたがそれぞれあるでしょうが、RailsにはrakeコマンドでDB作成Toolを作成することもできます。それを使用すればマルチDBの作成が夢ではないと思いますが、それは次回のテーマに譲ることにして、ここでは、使用するDBをMySQLとした場合、InstantRails導入した際に利用できるphpMyAdminを使用して簡単に作成してしまいましょう。InstantRails.exeが立ち上がっている状態であれば、以下のURLにアクセスできるはずです。

http://127.0.0.1/mysql/

今回作成するDBは三種類です。

  • hacks_development
  • hacks_test
  • hacks_production

これは先ほどのプロジェクトの作成の際に自動で命名規約されているDB名です。もちろんこれを変更することも可能ですが、それは以下のリソースに定義してあります。まず素直に自動生成されたソースを覗いてみましょう。

C:\InstantRails\rails_apps\hacks\config\database.yml

development:
  adapter: mysql
  database:hacks_development
  username: root
  password:
  host: localhost

test:
  adapter: mysql
  database:hacks_test
  username: root
  password:
  host: localhost

production:
  adapter: mysql
  database:hacks_production
  username: root
  password:
  host: localhost

今回はここでデフォルトに指定されているDB名をそのまま使用することにします。DB名を変えたければ、[database:]の値を変えればいいでしょう。

ちなみに、今回はMySQLなのでこのまま使用しますが、Oracleなどの場合は、DBを複数作成するよりも、スキーマを3つ用意したほうが現実的だと思われます。

【補足:文字コード問題】

my.iniに指定したように、このdatabase.ymlファイルでも文字コードを指定します。3つのDB定義の最後の要素にそれぞれ以下を追加します。(※MySQL使用時には必要ですがOracle使用する場合は必要がないようです。)

 encoding: utf8

 また、文字コード問題というと、DBの文字コードだけでなく、Ruby自体の文字コード指定をする必要があります
今回のRailsアプリの文字コード指定は、「environment.rb」というファイルでの最初の行で設定します。

例)文字コードをUTF-8に指定するやりかた。

$KCODE = "UTF8"


もしくは、

$KCODE = 'u'


どちらも同じ意味です。最初の 1 バイトしか読み取っていないようです。しかも大文字小文字すら関係ないようです。でも自分としては可読性を考慮してUTF8と記述したいところです。

【参考サイト】
http://www.ruby-lang.org/ja/man/html/_C1C8A4DFB9FEA4DFCAD1BFF4.html

 

では、phpMyAdminのサイトにてこれらの3つのDBを作成してみましょう。

確認は、phpMyAdminでもできますが、コンソールからDBが作成されたことを確認してみます。

C:\InstantRails\rails_apps\hacks>mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 5.0.27-community

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show databases;
+----------------------------------------+
| Database                               |
+----------------------------------------+
| information_schema                     |
|hacks_development                      |
|hacks_production                       |
|hacks_test                             |
+----------------------------------------+
29 rows in set (0.01 sec)

mysql>

 

 【eclipse へインポート】

この辺で、今後の作業の円滑を図り、eclipseへプロジェクトごとインポートしておきます。

 

プロジェクトを選択

選択してOK押下

作成されたことを確認

プロジェクトをマウントしたら、すかさずeclipseプロジェクト上の文字コードをUTF8に設定する。

 

 

 

【テーブルの作成】
事前準備してあるテーブル定義書に従い、Rails用のDDLファイルといえるマイグレーションファイルを作成します
作成の手順は、Rails 1.2.3 と Rails 2.0.2 では微妙に異なります

Rails 1.2.3では、

  1. ジェネレータの「migration」サブコマンドにてマイグレーションファイルのスタブを作成して後
  2. テーブル定義書に従い、マイグレーションファイルの編集
  3. db:migrate」コマンドを実行する。
  4. その後、「scaffold」を実行し、テーブルに対応するモデルクラスやコントロールクラスのCRUD構成を一気に作成する。


という手順になるが、Rails 2.0.2では

  1. scaffold」にて、MVCアーキテクチャのすべてのソースを一気に作成する。
  2. db:migrate」コマンドを実行する。


ただし、その際の「scaffold」のコマンドオプションの指定の仕方が変わりました。カラム名や型を同時に宣言しなければならなくなりました。

ここでは、Railsのヴァージョンは 1.2.3 なので、最初の手続きによるやり方で行う。
ちなみに、eclipse の RadRailsパースペクティブにおいては、generatorというviewが存在し、
そこでコマンドを実行が可能です。

ここでは、コマンドラインでの実行方法を紹介します。(Rails 1.2.3の場合)

C:\InstantRails\rails_apps\hacks>ruby script/generate migration create_kilowatts
      create  db/migrate
      create  db/migrate/001_create_kilowatts.rb

C:\InstantRails\rails_apps\hacks>

ちなみに、Rails 2.0.2の場合は migrationではなく、ここでいきなりscaffold していいです。

C:\InstantRails\rails_apps\hacks>ruby script/generate scaffold kilowatt kdate:text kwatts:integer

では、eclipse に戻って、作業領域をリフレッシュ(F5)した後、編集してみましょう。

001_create_kilowatts.rb(Rails 1.2.3の場合)

class CreateKilowatts < ActiveRecord::Migration
  def self.up
    create_table(:kilowatts) do |table|
        table.column  :kdate , :text
        table.column  :kwatts , :integer

    end
  end

  def self.down
    drop_table :kilowatts
  end
end

001_create_kilowatts.rb(Rails 2.0.2の場合)

class CreateKilowatts < ActiveRecord::Migration
  def self.up
    create_table(:kilowatts) do |table|

        table.text :kdate
        table.integer :kwatts
 
    end
  end

  def self.down
    drop_table :kilowatts
  end
end

(※注意)

Rails1.2.3とRails2.0.2の表記の違いに注意してください。また2.0.2の場合、自動生成される「table.timestamps」ですが、これはカラムの「create_at」と「update_at」に相当します。

このマイグレーションファイルはマルチDBに対応したRails独自の文法による記載になります。

非常に便利なものだが、まず各ベンダーのRDBの型との対応に注意しなければなりません。

手元には、データ型の対応表が必要になると思われる。

説明 MySQL SQL Server Oracle
id プライマリキー int(11) int NUMBER(38)
:string 文字列 varchar(255) varchar(255) VARCHAR(255)
:text 長い文字列 text text CLOB
:integer 整数 int(11) int NUMBER(38)
:float 浮動少数 float real NUMBER
:decimal 精度の高い小数 decimal(10,0) decimal(18,0) NUMBER(38)
:datetime 日時 datetime datetime DATE
:timestamp 日時(より細かい) datetime datetime DATE
:time 時間 time datetime DATE
:date 日付 date datetime DATE
:binary バイナリデータ blob image BLOB
:boolean Boolean型 tinyint(1) bit NUMBER(1)
create_at マジックフィールド datetime datetime DATE
update_at マジックフィールド datetime datetime DATE

 

次に「rake db:migrate」コマンドを実行する。(ちなみに eclipse の Rake view からも実行が可能だ。)

C:\InstantRails\rails_apps\hacks>rake db:migrate
== CreateKilowatts: migrating =================================================
-- create_table(:kilowatts)
   -> 0.0780s
== CreateKilowatts: migrated (0.0780s) ========================================

 この時、「テーブル: schema_info」も同時に作成されていることに着目しておこう。これはマイグレーションにおける履歴管理のためのテーブルで、Railsの管理の下、テーブル定義が仕様変更となっても、すぐに反映させたり、また逆に元に戻すことを可能にした仕組みだ。

この点を踏まえると、Railsを使うならば、マイグレーションなしに勝手にテーブル作成することは好ましくないといえるだろう。

【テーブル作成の確認】

C:\InstantRails\rails_apps\hacks>mysql -u root
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 30 to server version: 5.0.27-community

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>use hacks_development;
Database changed
mysql>show tables;
+-----------------------------+
| Tables_in_hacks_development |
+-----------------------------+
| kilowatts                   |
|schema_info                 |
+-----------------------------+
2 rows in set (0.00 sec)

mysql>

【足場の作成】

ここで足場となるMVCアーキテクチャに沿ったソースを自動生成します。そのためのコマンドとして、先ほど紹介した「scaffold」というコマンドがあります。

C:\InstantRails\rails_apps\hacks>ruby script/generate scaffold kilowatt
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/kilowatts
      exists  app/views/layouts/
      exists  test/functional/
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/kilowatt.rb
      create    test/unit/kilowatt_test.rb
      create    test/fixtures/kilowatts.yml
      create  app/views/kilowatts/_form.rhtml
      create  app/views/kilowatts/list.rhtml
      create  app/views/kilowatts/show.rhtml
      create  app/views/kilowatts/new.rhtml
      create  app/views/kilowatts/edit.rhtml
      create  app/controllers/kilowatts_controller.rb
      create  test/functional/kilowatts_controller_test.rb
      create  app/helpers/kilowatts_helper.rb
      create  app/views/layouts/kilowatts.rhtml
      create  public/stylesheets/scaffold.css

 

 

ここまでで雛形が完成したはずです。

【動作確認】

では、早速サーバを起動させてみましょう。WebサーバはRailsでは複数用意されていますが、開発モード時点ではApacheなどは使わないです。(かといって現在あがっているApacheを落とす必要はありません。)簡易サーバとして、Mongrelサーバというものが、ありますので、今回はそれを使用します。

サーバー起動のやり方は、コマンドラインとeclipseでの起動のさせ方とありますが、ポート番号まで比較的簡単に指定できるeclipse起動のやり方を紹介します。

まず、eclipseにて対象となるプロジェクト(今回の場合は「hacks」)を選択して、Runします。

 

ここでOKを押下すれば、対象のサーバインスタンスが立ち上がるはずなのですが、実際には別のインスタンスが起動してしまうような現象に遭遇しました。ヴァージョンは、3.2.1でも3.4.1でも起こります。なのでその場合は、一旦関係のないサーバインスタンスを停止し、再度Serversヴューにて起動対象のサーバインスタンスを選択して実行してみてください。

以上のように起動すれば、Webアプリが起動されているはずです。

以下のURLをブラウザで指定してみましょう。

(今回はIP指定しますが、普段の開発ではHOSTはlocalhostか127.0.0.1でかまわないでしょう。)

// 今回のURL
http://192.168.1.29:3008/kilowatts/

// 通常はデフォルト指定のままなので以下のURL
http://localhost:3008/kilowatts/
http://127.0.0.1:3008/kilowatts/

ホスト名の次に指定するURIの要素がテーブル名になっていることに着目してください。

また、デフォルトの入り口が、indexになるのですが、実際にはlist画面にリダイレクトされています。ここは、Railsの仕組みをもう少し追いかけていくことで判明します。

 

とりあえず、ここまでで Railsのアプリの土台部分がが作成できました。

 

おめでとうございます!!

 


Ajaxライブラリの導入方法

Ajaxには多数のライブラリが存在しており、現在では生で「XMLHttpRequest」オブジェクトを操作することはあまり機会がなくなっているといえるんではないでしょうか。最も有名なライブラリは「Prototype」ですが、これはどうやらRuby On Railsがそのルーツだそうです。ということで Railsをやるならデフォルトでついてきてます。

<%= javascript_include_tag:defaults%>

 この記述でHTMLソースにはどのように変換されているか調べてみると、

<script src="/javascripts/prototype.js?1224125580" type="text/javascript"></script>
<script src="/javascripts/effects.js?1224125580" type="text/javascript"></script>
<script src="/javascripts/dragdrop.js?1224125580" type="text/javascript"></script>
<script src="/javascripts/controls.js?1224125580" type="text/javascript"></script>
<script src="/javascripts/application.js?1224125580" type="text/javascript"></script>

 でした。

自動生成された javascriptライブラリをこの一行でロードしています。一番お手軽な書式ですが、必要なものだけを指定したほうが効率がいいでしょう。その場合は、

<%= javascript_include_tag"prototype.js"%>

ところで、このロードの順番は重要です。前後を入れ替えないこと。

逆に言うと、自分で定義した javascript関数は一番最後にロードされる「application.js」に入れるべきです。

 

XMLHttpRequestの使い方

XMLHttpRequestの使い方を調べるためにサンプルを作成しましょう。
RailsではXMLHttpRequestを生で使わないとはどういうことでしょうか?

ひとつには、ライブラリ「prototype.js」を内部で利用しているため、そのラッパークラスを通じて操作できるためです。

しかし、Railsの場合は、さらにそれをラッピングしたメソッドが存在し、それを利用することができます。

まず、それを確かめるためのコントロールとヴューを作成したいと思います。
ここで、今回はDBを使わないので、あえてscaffoldはせずに、generateではコントローラだけを指定してモデルは作成しないことにします。必要なのはアクションとその画面のみ…ということで、このようなコマンドを実行してみます。

C(コントローラ):hacks
V(ヴュー):monitor

C:\InstantRails\rails_apps\hacks>ruby script/generate controller hacks
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/hacks
      exists  test/functional/
      create  app/controllers/hacks_controller.rb
      create  test/functional/hacks_controller_test.rb
      create  app/helpers/hacks_helper.rb

コントローラですが、hacks_controller.rb はJavaでいえばサーブレットみたいなものです。URIにも反映されます。V(ヴュー):monitorはまだここでは作成してませんが、アクション名でもあり、コントローラに実装するメソッド名でもあり、Viewとしてのrhtmlファイル名であったりします。この命名規則がしっかりしたところが、Railsが優れた生産性を誇る仕組みでもあります。この恩恵は非常に効率的ですね。MVCに着眼すれば、これからの作業の太枠が決まります。

  •  コントローラのスタブメソッド作成
  • ヴューの作成
  • URLの決定

まずアクセス先のURLを決めてしまいます。

http://192.168.1.29:3008/hacks/monitor

にすることにして、

コントローラのスタブメソッド作成します。(file:///C:/InstantRails/rails_apps/hacks/app/controllers/hacks_controller.rb

class HacksController < ApplicationController
  def index
    #
    # URI省略時のインデックスページの処理。特定のアクションにたらい回します。
    #
    monitor
    render :action => 'monitor'
  end
  def monitor
    #
    # ここに処理を書きます。アクセス時の業務処理です。
    #
  end
  def tmp
    #
    # ここに処理を書きます。まだメソッド名も決めてませんが「送信ボタン」に対応するアクションです。
    #
  end
end

 

ヴューの作成

レイアウトをまず決めます。対応するコントローラに対して1本作成します。

file:///C:/InstantRails/rails_apps/hacks/app/views/layouts/hacks.rhtml

これは、外枠だけの入れ物なので、既存のレイアウトをそのまま複製してしまいます。タイトルくらいはそれらしく編集しておきましょう。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <%= javascript_include_tag :defaults %>
  <%= stylesheet_link_tag 'scaffold' %>
  <title>Ajax呼び出し時の内部状態<%= controller.action_name %></title>
</head>
<body>

<p style="color: green"><%= flash[:notice] %></p>

<%= yield  %>

</body>
</html>

yieldに入るアクションごとの部品ヴューは

file:///C:/InstantRails/rails_apps/hacks/app/views/hacks/monitor.rhtml

になるでしょう。

内容は、

<h1>Monitor</h1>

<% form_tag :action => 'tmp' do %>
  <%= render :partial => 'form' %>
  <%= submit_tag '送信' %>
<% end %>

な感じにしておきます。送信ボタン押下時のメソッド名をそろそろ決めておかないといけませんが、とりあえず、こうしておきます。

 画面自体は出ることをサーバにて確認できます。

では、ここからいよいよメインディッシュにんります。RailsでAjaxを使うには、対応するいくつかのメソッドが用意されています。XMLHttpRequestでは「form_tag」は使用しないのです。その代わりに「form_remote_tag メソッド」を使用します。

form_remote_tag メソッド

XMLHttpRequestの機能を活用するためのメソッドです。

基本構造は、

<%=form_remote_tag(...param...) %>
  <div></div>
  <p>
  <%= submit_tag '送信' %>
  </p>
<%=end_form_tag%>

 パラメータ部分は多少複雑です。ハッシュ形式の指定方法です。

それぞれのキー項目をみていきます。赤字は必須指定。

  • :update => ""
  • :url => {}
  • :position => ""
  • :success => ""
  • :loading => ""
  • :loaded => ""
  • :interactive => ""
  • :failure => ""

:update => "表示要素のid属性値"

なんだけれどもここは結構重要です。肝です。:complete:success指定との違いがわかりづらいのです。ここに参考にできる記事がありますが、もっと調べておく必要性を感じます。

 

:url => { :action => リクエストを処理するアクションのシンボル }

:position => "挿入位置"

:success => "XMLHttpRequestのreadystateが成功時"

ちなみに、readystateがsuccess というのは厳密にはないはずなのだが、指定できるそうだ。completeとほとんど似た状態だが、わずかに:successのほうが早い状態になります。

:complete => "Element.toggle('id属性値')"

readystate

  状態 意味
0 uninitialized 初期化されていない
1 loading 読み込み中
2 loaded 読み込み完了
3 interactive 操作可能
4 complete 準備完了

 

file:///C:/InstantRails/rails_apps/hacks/app/views/hacks/monitor.rhtml

 <%= form_remote_tag (
    :update => "complete",
    :url => { :action => :zero_update },

    :position => "top",
    :success => "$('success').innerHTML='成功; ステータスコード='+request.status",
    :loading => "$('loading').innerHTML='リクエスト送信中…; ステータスコード='+request.status",
    :loaded => "$('loaded').innerHTML='リクエスト送信完了; ステータスコード='+request.status",
    :interactive => "$('inter').innerHTML='レスポンス受信中; ステータスコード='+request.status",
    :failure => "$('failure').innerHTML='エラー; ステータスコード='+request.status") %>
  <h3>Ajax呼び出しの内部状態を表示します</h3>
  <div id="loading" style="font-size: 1.2em"></div>
  <div id="loaded" style="font-size: 1.2em"></div>
  <div id="inter" style="font-size: 1.2em"></div>
  <div id="success" style="font-size: 1.2em; color: green"></div>
  <div id="failure" style="font-size: 1.2em; color: red"></div>
  <div id="complete" style="font-size: 1.2em; color: green"></div>
  <p>
    <%=submit_tag'送信' %>
  </p>
<%=
end_form_tag %>

file:///C:/InstantRails/rails_apps/hacks/app/views/hacks/monitor.rhtml

   defzero_update
    #
    # ここに処理を書きます。まだメソッド名も決めてませんが「送信ボタン」に対応するアクションです。
    #
   render :text => "Ajax hello!!!"
  end

render :text => "Ajax hello!!!"

ここが遷移しない仕組みです。遷移先の画面指定などはしていないのです。この例ではサーバ側の固定文字列を返していますが、コントローラで料理ができるということは、「可変データを非同期に返せる」ということです。静的なサーバ上のデータを返したいだけであるならば、もっと別の手法があります。その手法は別のテーマで触れる予定です。

変換後のHTMLは

 <form action="/hacks/zero_update" method="post"onsubmit="new Ajax.Updater('complete', '/hacks/zero_update', {asynchronous:true, evalScripts:true, insertion:Insertion.Top, onFailure:function(request){$('failure').innerHTML='エラー; ステータスコード='+request.status}, onInteractive:function(request){$('inter').innerHTML='レスポンス受信中; ステータスコード='+request.status}, onLoaded:function(request){$('loaded').innerHTML='リクエスト送信完了; ステータスコード='+request.status}, onLoading:function(request){$('loading').innerHTML='リクエスト送信中…; ステータスコード='+request.status}, onSuccess:function(request){$('success').innerHTML='成功; ステータスコード='+request.status}, parameters:Form.serialize(this)}); return false;">
  <h3>Ajax呼び出しの内部状態を表示します</h3>

  <div id="loading" style="font-size: 1.2em"></div>
  <div id="loaded" style="font-size: 1.2em"></div>
  <div id="inter" style="font-size: 1.2em"></div>
  <div id="success" style="font-size: 1.2em; color: green"></div>
  <div id="failure" style="font-size: 1.2em; color: red"></div>
  <div id="complete" style="font-size: 1.2em; color: green"></div>
  <p>
  <input name="commit" type="submit" value="送信" />
  </p>

</form>

 内部では、prototype.jsのクラスが使用されているようです。

しかも変換されているのは、「Ajax.Request」ではなく「Ajax.Updater」のようです。ほとんど似た処理ですが、サーバ上のデータをページに非同期に挿入するのに使います。

非同期の持ち味を実感するには、クライアントサイドで「onSubmit」とか「onClick」とかでは、実感が薄いですね。

トリガーとなるイベントの種類や指定方法も整理しておくといいでしょう。

 その前に、:successと:completeの違いについて整理しておきます。上記のソースの実行結果は、実は問題があります。

:successと:completeの違いについて

どちらもサーバからレスポンスが返される準備が整った状態ではありますが、ステータスでいうと、まず:successになり、次に:completeになります。これを同じ意味と捕らえるのはもったいないことです。実際に:updateで指定された要素に結果が挿入されるのは、:completeのタイミングですが、そもそも非同期処理のため、ページが遷移せず、つまりは既読のデータ(id=completeで指定された領域のデータ)は消えないのですそのため、前回サーバより取得した値は消えずに残り、そこに追記という形で対象要素にロードされるため、意図した結果にならないでしょう。その場合に、:successのステータスにおいて対象要素の「過去の値」を消す処理を施すことができます。

 <%= form_remote_tag (
    :update => "complete",
    :url => { :action => :zero_update },
    :position => "top",
   :success =>"$('complete').innerHTML='';$('success').innerHTML='成功; ステータスコード='+request.status",
    :loading => "$('loading').innerHTML='リクエスト送信中…; ステータスコード='+request.status",
    :loaded => "$('loaded').innerHTML='リクエスト送信完了; ステータスコード='+request.status",
    :interactive => "$('inter').innerHTML='レスポンス受信中; ステータスコード='+request.status",
    :failure => "$('failure').innerHTML='エラー; ステータスコード='+request.status") %>
  <h3>Ajax呼び出しの内部状態を表示します</h3>
  <div id="loading" style="font-size: 1.2em"></div>
  <div id="loaded" style="font-size: 1.2em"></div>
  <div id="inter" style="font-size: 1.2em"></div>
  <div id="success" style="font-size: 1.2em; color: green"></div>
  <div id="failure" style="font-size: 1.2em; color: red"></div>
  <div id="complete" style="font-size: 1.2em; color: green"></div>
  <p>
    <%= submit_tag '送信' %>
  </p>
<%= end_form_tag %>

 

遷移させないということは

a

RailsでのAjax利用の構造

a

自作のJavaScriptはどこに書くのか?

link

サーバ内テンプレートを元にコンボボックス表示

a

リクエストの判別方法

a

DBの値を元にコンボボックス表示

a

定期的なリクエスト実行

定期的な実行を伴うAjaxの処理は、「PeriodicalExecuter」を使用しますが、Railsではこれをさらにラップして「periodically_call_remote」というメソッドを使用します。

ここでは、サンプルとして、クライアント側から非同期に、ブラウザを利用しているユーザからは意識させずに、サーバーにアクセスし、サーバ上にあるDBの最新情報を取得して、それを画面を遷移させることなく表示するサンプルを作成してみます。

対象の画面:index3

イベント:タイマー

定期実行時間:3秒

リクエスト先のコントローラ:greeting

リクエスト先のアクション:select_todos_by_mysql

レスポンス先のエレメントID:complete

とすると、

1、Viewは(greeting/index3.html.erb)

         <hr/><%=periodically_call_remote(:update => "complete", :frequency =>3,
        :url => {:action => :select_todos_by_mysql},
        :position => "top",
        :success => "$('complete').innerHTML = ''",
        :loading => "",
        :loaded => "",
        :interactive => "",
        :failure => "") %>
        <div id="success" style="font-size: 1.0em; color: green">
        </div>
        <div id="complete" style="font-size: 1.0em; color: green"></div>

2、コントローラは(greeting_controller.rb)

   def  select_todos_by_mysql

    if request.xml_http_request?()
      @stmt = @con.createStatement
      rs = @stmt.executeQuery("select * from todos")
      str = ''
      while rs.next
        str += rs.getString(3)
        str += "<br />"
      end
      puts str
     render :text => str
    end

  end

になります。(ここでDB接続と切断は他のアクションでも共通処理になるはずなので外だしすることにします。※後述します。)ここで、request.xml_http_request?()は非同期でリクエストされた時のみ処理するための記述です。

機能の内容をみてみましょう。

 periodically_call_remoteメソッド

  • :update => レスポンス先のエレメントID指定,
  • :frequency => 秒数,
  • :url => {:action => :アクション名(:メソッド名)},
  • :position => 挿入場所,
  • :success => レスポンス完了前の直前処理,

変換後のHTMLは

<script type="text/javascript">
//<![CDATA[
new PeriodicalExecuter(function() {new Ajax.Updater('complete', '/greeting/select_todos_by_mysql', {asynchronous:true, evalScripts:true, insertion:Insertion.Top, onFailure:function(request){}, onInteractive:function(request){}, onLoaded:function(request){}, onLoading:function(request){}, onSuccess:function(request){$('complete').innerHTML = ''}, parameters:'authenticity_token=' + encodeURIComponent('e4a2e3f8ab38073ed2a9d8acaced418f8042a651')})}, 3)
//]]>

</script>
<div id="success" style="font-size: 1.0em; color: green"></div>
<div id="complete" style="font-size: 1.0em; color: green"></div>

これだけの記述で、タイマーによる非同期処理が実現できます。

なお、DBアクセスの部分は、今回「ActiveRecordを使わないやりかた」として、通常の「DriverManagerによる接続」を実装してみました。しかし、これはJrubyOnRailsによるJDBC接続となるため、ここでのトピックとしてのテーマとずれますが、実現方法はいたって簡単です。使用するDBのJDBCドライバを使用するRubyのlibフォルダに格納し使います。

 DB接続と切断をアクション共通処理としてくくりだしたいので、

  1. DB接続(共通処理:除外するアクションも指定可能)
  2. アクション:DB操作
  3. DB切断(共通処理:除外するアクションも指定可能)

こういう処理の流れが太枠になります。この構成をRailsで行うには、事前処理と事後処理ということで、

  # DB接続部分は外だしし、メソッドの前に実行させる。
  before_filter :db_connect, :except => [:index,:index2,:index3,:index4]

  # DB切断は外だしし、メソッドの前に実行させる。
  after_filter :db_close, :except => [:index,:index2,:index3,:index4]

 を定義し、

  private

  def db_connect
    begin
      java.lang.Class.forName('com.mysql.jdbc.Driver').newInstance
      @con = java.sql.DriverManager.getConnection('jdbc:mysql://localhost/JRoR2MySQLTom01_development', 'root', '');

      #    rescue => ex
      #      @message = 'error occureed !!!'
      #    ensure
      #      @con.close if @con
    end

  end

  def db_close
    @con.close if @con
  end

 を実装します。この処理は「除外指定されていないメソッド」である「select_todos_by_mysql」の前後に差し込まれて実行されるため、DBの接続切断処理が自動的に行われる結果となります。

 

 

リクエスト情報

a