Ruby・RoRの最近のブログ記事

 久しぶりに(前回の記事)RoR(Ruby on Rails)の勉強をしました。今回は、再度RoRを勉強直してまとめた備忘録です。


●Railsのコンポーネント
・Active Record―モデル(M)
・Action View―ビュー(V)
・Action Controller―コントローラ(C)

・Active Support―共有ライブラリ集
・Action Mailer―電子メールの送受信
・Action Web Service―ウェブサービス

Action ViewとAction Controllerをまとめて「Action Pack」とも呼ばれている。


●Railsの設計哲学
・DRY(Don't Repeat Yourself:繰り返しを避けよ)
 DRY原則とも呼ばれ、繰り返しの記述を避けることで、無駄を省き、バグを減らし、効率を高める。

・設定より規約(Convention over Configuration)
 たとえば、モデルには命名規則があり、テーブル名がusersのように複数形ならクラス名は単数形のuserであり、クラスのファイル名は、user.rbとなる。DHH(David Heinemeier Hansson)氏は、「制約は事由をもたらす」と語っています。


●気になる事
・エンコード
 Ruby―「Shift-JIS」
 RoR―「UTF-8」。

・シンボル
 シンボルは、どこでも同一のオブジェクトを表わすことができるようにしたRubyの機能です。名前に「:」を付けるとシンボルになります。
例えば、

hoge = "test1"
piyo = "test1"

と記述した場合、"test1"のオブジェクトIDは、それぞれ違いますが、

hoge = :test2
piyo = :test2

と記述した場合、:test2のオブジェクトIDは、全て同じです。主に、ハッシュ(連想配列)のキーに使用されています。RoRでは、セッションの記憶に使うハッシュで特に使われています。

・YAML(ヤムル)
 YAMLは「YAML Ain't Markup Language」(YAMLはマークアップ言語ではない)の略とされています。正式名からわかるようにマークアップ言語で、複雑であるXMLとは違うことを意味していて、扱いやすく設定されているのが特徴です。Rubyでは、バージョン1.8からライブラリを利用する事により簡単に扱うことが出来ます。
例:info.yml

-
    company: 日本シー・エー・ディー
    section: IT事業部
    team: WEB-DBチーム

例:info.rb

require 'yaml'

data = YAML.load(File.new('info.yml'))

data.each do |item|
    puts item['company']
    puts item['section']
    puts item['team']
end

出力結果:

日本シー・エー・ディー
IT事業部
WEB-DBチーム

・Rake(レイク)
 Rubyで書かれたビルドツール。

 アンケートシステムの番外編です。番外編を書く暇があったら、アンケーシステムを完成させろよと言われそうですが、気にせず書いていきます。

 以前の記事でアンケートシステムを簡略化すると書きましたが、どれくらい簡略化するかと言うと、一つしかアンケートを出来ないようにします。アンケートごとに関する表示を作成するのが面倒くさいので。メニューにするとこんな感じです。

1.アンケートの作成
2.アンケートの投稿
3.項目の一覧を表示
4.アンケートを削除

「1.アンケートの作成」で作成又は修正する感じです。作り的には、後々複数投稿するシステムに変更するのに、それほど難しくない作りだと思います。Rubyでプロトタイプをぼちぼちと作成しているのですが、作成して思った事を今回の記事では書きます。

 

 Javaで作成する事に慣れているので、やはりオブジェクト指向で考えてしまいます。Rubyもオブジェクト指向ですが、Javaとは違う箇所やJavaの作りでは出来ない処理も出来てしまいます(Rubyではそのような作りになっているので当然ですが)。

Javaでは、すべての作りがクラスで作成するようになっています。ちょっと待てよ、インタフェースは?と思う方もいるかと思いますが、インタフェースでは処理は作成できません。C言語でも、C言語はすべて関数で作成されているといった場合に、ヘッダファイルやマクロがあると反論しないと思います。実際マクロや構造体を処理するのは関数の中ですから、C言語はすべて関数で作成されているのだと言えます。マクロの定義は、オブジェクトファイルですべて置き換わっていますし、構造体はメモリ上に領域が確保されるだけです。

今、考えているのは、画面の情報を画面フォームオブジェクトに格納し、実際処理を行って、DBに格納するポーターオブジェクトに代入します。ポーターオブジェクトを使いDBで処理を行います。DBからデータを取ってくる場合は、この考え方の逆で行います。列挙すると以下の感じです。

1.画面から情報を受け取り、画面フォームオブジェクトに格納する。
2.画面フォームオブジェクトのデータを使い、処理を行う。
3.画面フォームオブジェクトをポーターオブジェクトに代入する。
4.ポーターオブジェクトのデータを使い、DAOでDBの処理をする。

上記の考えは、MVCの考え方や三層構造の考え方です。

画面フォームクラスは、画面の情報を基に作成されます。画面とプログラム(VIEW)のデータの橋渡しです。変数名や日本語名は画面名から導き出します。画面が変更されれば、画面フォームクラスも変更されます。又、DBのテーブルや項目の変更に影響されません。

ポータークラスは、DBの情報を基に作成されます。DBとプログラム(DAO)のデータの橋渡しです。変数名や日本語名はDBのテーブルや項目から導き出します。DBのテーブルや項目が変更されれば、ポータークラスも変更されます。又、画面の変更に影響されません。

作りとしては、オブジェクト指向の考え方に合致していますね。

 考え方として問題となっているのは、処理の部分です。オブジェクトで共通する処理がある場合を考えていきたいと思います。Javaでは、共通した処理を使いたい場合に、しばしば親クラスを作成し、継承させてクラスを作成します。それが、「is a」の関係になっていれば、良いのですが、必ずしもそうとは言えません。本来共通した処理がある場合は、処理だけを独立されてクラス等に使わせる必要があります。Javaなら処理するクラスを作成して、そのクラスを各クラスで使用しても良いと考えているのですが、親クラスで定義させることが多いですね。一般的に言われているユーティリティークラスはそのようになっているのですが...

 Rubyでは、Mix-inが利用できます。Mix-inとは、モジュールをクラス等に取り込むことで、クラスならクラスで定義したメソッド等と同じように操作できる事です。このため、クラス間で共通の処理がある場合は、モジュールで作成し、各クラスが共通のモジュールを取り組めば良いことになります。その事により(Javaとは違い)、無理やり継承(is aの関係)させる必要はなく、共通処理を行わせることができます。この考えは、オブジェクト指向の「has a又はpart of」の関係も実現する事が出来ます。Javaでは、「has a又はpart of」の関係を実現させるには、内部クラスで実現できますが、他のクラスで使用する場合は結局、継承させる必要があります。

 又、Javaのインタフェースを適切に利用しているシステムも少ないと思います。インタフェースの役目として、C言語のヘッダファイル(プロトタイプ宣言や定数の作成)、ポリモルフィズムの実現にも利用されなければなりません。


 好き勝手に書いてきましたが、理想と現実は違うという事ですね。と言っても、私に理想的な設計できる能力があるかと言われると、勉強中としか答えられませんが。Rubyのアンケートシステムは早ければ今月中に完成させる予定でいます。

 前回のRubyの記事(Rubyで作成するアンケートシステム MySQL編)では、RubyからDBの操作を紹介しました。今回は、アンケートシステムで使用するDBの設計書を紹介します。今回はRubyと関係がありませんが何かの題材にでも。


 勉強用なので、設計書と言ってもERDとSQL文です。テーブル設計は無く、あるのはMySQLとPostgresSQLで試したSQL文ぐらいです。SQL文は、主にMySQLに対応していますが、テーブル作成部分は、PostgresSQLでも動作を確認しています。

ファイルにまとめました。説明もファイル中にあります。
QNS_RD.xls
テーブル作成.sql

 前回の記事(Rubyで作成するアンケートシステム Postgres編)では、PostgresのCRUD操作を投稿しましたが、日本語に対応できていませんでした。そこで、RubyやRailsで標準であるMySQLを使用することにしました。今回は、MySQLでのCRUD操作を紹介します。もちろん日本語対応です。

 Windowsでは、MySQL側を特に指定しなく、Ruby側でもオプションなどを指定しないくても日本語が表示されました。次回のRubyの記事では、アンケートシステムのDBの情報を投稿するつもりです。

 前回のRubyに関する記事(Rubyで作成するアンケートシステム ファイルの読み込み編)では、ファイルの読み込みを紹介しましたが、今回は、DB関連です。DBにも当然いろいろありますので、その中のPostgresの操作を紹介します。

 Postgresの操作を紹介するのは、CRUD操作です。ただ、私が使用しているバージョンでは日本語が扱えません。原因は分かっているのですが、とりあえず問題を置いて紹介しておきます。

 

require "postgres"

# 表示メソッド
def view(conn)
    res = conn.exec("SELECT * FROM USER_INFO;")
    res.each{|items|
        print items[0]
        (items.size-1).times{|i|
            print ",", items[i+1]
        }
    }
    print "\n"
end


conn = PGconn.connect("localhost",5432,"","","enquete","enquete","enquete")
# 本来は、ここでクライアントのエンコードを設定する
# conne.set_client_encoding("utf-8")

begin
    # データの挿入
    conn.exec("INSERT INTO USER_INFO(USER_ID,USER_NAME,PASSWORD)VALUES('user1','user1','pass');")

    view(conn)

    # データの更新
    conn.exec("UPDATE USER_INFO SET USER_NAME = 'user-1' WHERE USER_ID = 'user1';")

    view(conn)

    # データの削除
    conn.exec("DELETE FROM USER_INFO WHERE USER_ID = 'user1';")

    view(conn)
rescue => ex
    conn.exec("ROLLBACK;")
    print ex.message, "\n"
ensure
    conn.close()
end

 例外処理を設けて、ロールバックしていますが、例えば、挿入後に例外が発生して、ロールバックしても挿入はコミットしてしまいます。たぶんプログラム上で、自動コミットしないようにしないといけないのですが、その情報もまだ知りません。やはり、postgresでは、情報が少ないので、そろそろ区切りをつけてmysqlで素直に作成しようと考えています。

 クライアントのエンコード設定は、postgresドライバのバージョンによって設定が違っているようです。今回使用したのは、0.4.0で、Webの情報によると0.7.1では「set_client_encoding」メソッドが使用できるようです。ただ、gemでアップデートしようとしましたが、失敗しました。どうやら他も同時にアップデートして、バージョンをそろえないといけないみたいです。情報は現在こんなところです。

 Rubyで作成するアンケートシステム 仕様編の記事でアンケートシステムにはファイルを入出力するが必要という事で今回はファイルの読み込みを投稿します。

# ファイルの読み込み(IOクラスの制御なし)
open("db/UserInfo.txt").each{|line|
    line.chomp!     # 行の末尾の改行文字を削除
    dates = line.split(",")
   
    dates.each{|date|
        puts date
    }
}

# ファイルの読み込み(IOクラスで制御あり)
open("db/UserInfo.txt"){|io|
    io.each{ |line|
        line.chomp!     # 行の末尾の改行文字を削除
        dates = line.split(",")
       
        dates.each{|date|
            puts date
        }
    }
}
 

2つの処理を作成しました。

  「ファイルの読み込み(IOクラスの制御なし)」では、open()メソッドを呼び出し、eachメソッドでテキストの1行をline変数に代入しています。ちなみに「#」以降は、コメントになります。C及びJavaの「//」と考えて下さい。「line.split(",")」で配列を取得し、eachメソッドでデータを1つ1つ表示させています。

 「ファイルの読み込み(IOクラスの制御あり)では、IOクラスにデータを代入して、それから処理をしてます。IOクラスを使用することによって、読み込みだけではなく、書き込みやファイルポインタを使った制御が行えます。

 他の言語でファイル処理をした事がある方は、何か足りない事に気が付くと思います。openメソッドを呼び出しているのに、closeメソッドらしきものが呼び出されていない。Rubyでもcloseメソッドは存在し、closeメソッドを呼び出す必要があります。ただ、今回の場合は、ファイルをopenメソッドで呼び出し、{}のブロックの中で処理を行わせています。よって、ブロックを閉じると自動的にclosメソッドが処理されるため、ブロックに渡した場合は、closeメソッドを呼び出す必要はありません。Rubyでは、このように、ブロックにデータを渡して処理させることが多々あり、ファイル処理ではcloseメソッドの書き忘れが無いメリットがあります。

 ファイルで作成すると前回の記事(Rubyで作成するアンケートシステム 仕様編)で書きましたが、やはりDBで処理する方が楽という事で、RubyでDBのアクセスができるように情報を集めました。次回のRubyの記事では、DBの操作に触れたいと思います。

 私が所属するWEB-DBチーム内では、新たな言語やフレームワークなどを取得する為に、お題としてアンケートシステムの作成を各自で進めています。私は始めにRuby on Railsで作成しようとしましたが、まずは、Rubyの基礎をつけるという事で、Rubyでアンケートシステムを作ることにしました。どう考えてもRubyで作る方が時間がかかると思いますが、Rubyの勉強にもなるという事で...。


 アンケーシステムの全体の動きとしては、最初に認証画面が表示され、認証後はメニューが表示されると考えています。
メニューには、


1.アンケートの作成
2.アンケートを選ぶ
3.アンケート一覧

のようにアンケートを選ぶメニュー、後は項目の作成、修正及び削除などのように階層的に展開する。

アンケートとして認証するユーザや投票結果などを格納する物としてはどうするかですが、DBに接続しないで、基本らしくテキストで処理しようと考えています。あまり複雑な事はしたくないので、テキストは次のように分け、中身は次のようにします。

1.ユーザ情報
 ユーザID,ユーザ名,パスワード
2.アンケート
 アンケートID,アンケート名,期限
3.アンケート項目(各アンケート)
 項目名,投票数
4.ユーザ投票記録
 アンケートID,ユーザID[,ユーザID]

 先ほど、説明していませんでしたが、アンケートシステムは匿名投票を考えています。投票は、1つのアンケートに対して1回だけと制限する為に、ユーザ投票記録に記録します。既にアンケートIDに一致するユーザIDがあった場合は投票できない事にします。DB無しで制御すると面倒ですが、そこはあまり考えない事にします。ユーザIDは投票したらどんどん後ろに追加する形で制御します。

 次に、Rubyでテキスト処理の勉強が出来ていないので、まずは、テキスト処理から取り掛かることにします。次回のRubyの投稿でテキストの読み書きを投稿する予定でいます。

 効率 その2として、効率の面で見たJavaとRubyの違いを書きます。

 はじめに、その1でも紹介しましたアクセサの違いから紹介します。Javaでは、アクセサを作成するのに、メソッドのなどのコメントを省くにしてもEclipseなどのIDEを使用しないと効率的に作成できませんでした。Rubyでは、以下の記述だけでアクセサが可能です。

attr_accessor:変数名

 Rubyを簡単に紹介すると、プログラミングにデフォルトを用意し、デフォルトと違う処理だけを独自に作成すると言えます。デフォルトとは、「attr_accessor:変数名」のように決まった処理の事で、アクセサ設定の場合に限れば、他にも「attr_reader:変数名」や「attr_writer:変数名」があります。それぞれ説明すると、最初がアクセサ、次にゲッタ、最後にセッタの設定です。これらの指定は、変数の値を取得・設定をする場合に使用し、デフォルト以外の処理をする場合(例えば、範囲チェック)は「attr_xxxx:変数名」を指定せずにJavaのようにメソッドを作成して、プログラミングを行います。

 デフォルトと説明しましたが、Rubyではデフォルトとは記述されていなくUnixやCなどを行った人であれば当然の事を暗黙的に決める事です。暗黙的な所がデフォルトと言えます。この事が詳しく書かれている記事に、ITproさんの記事がありましたので、URLを書いておきます。URLは、以下の通りです。



 続いて、「Ruby on Rails」(以下、RoRと記述する)での場合です。RoRは、ITproさんの記事にも書かれていますが、Rubyよりもよりデフォルトを多く設定したとも言えます。ここらへんの説明は、ITproさんの記事に譲るとして、効率の部分で話を続けます。

 RoRは、JavaのStrutsのようにWebを効率よく作成するためのフレームワークですが、デフォルトの場合では、ほとんど自動的に行ってくれます。何かWebを作成するのであれば、そのデフォルトを改造して作成する形で進めていきます。また、RoRを便利に使用するためのGUIツールも存在し、DBの設定までもGUIで行う事により、キーボードを数回たたくだけでWebを作成する事も可能です。

 では、RoRを使用する事によって、Webを簡単にできる手順を紹介します。今回は、GUIツールを使用しないで説明しますが、本来のRoRだけでも簡単に作成できる事が分かると思います。

(1)アプリケーションを作成
 アプリケーションを作成するには、コマンドプロンプトやシェル上で「rails -d DB名 アプリケーション名」と入力し実行する。実行すると、アプリケーションの枠組みが作成されます。アプリケーション作成時に、既にオプションとしてDBを選択する事により、DBの設定も行ってくれます。
(2)DBの基本的な設定
 続いて設定するのは、早くもDBの設定です。DBの設定は、例えば、データベースやユーザの作成であり、既に利用するデータベースとユーザが決まっていれば不要です。GUIツールを利用すれば、コマンドプロンプトやシェルからの記述は不要です。
(3)RoR上でのDBの設定
 RoRでのDBの設定は、自動作成したアプリケーションの枠組みである「config/database.yml」という所に、DB名、ユーザ名、パスワード、ホストを設定すれば、完了です。ちなみに、RoRでは、オプションの設定にもよりますが、一般的な設定でも開発用、テスト用、本番用のDB設定ができるように定義されています。
(4)モデルの作成
 続いては、モデル(MVCのモデルの事)の作成です。モデルの作成は、Rubyを使いコマンドは、「ruby script/generate model クラス名」だけです。自動作成されたところに、カラム名を記述すれば終わりです(アクセサの説明でしたように簡単に設定できます)。先度DBでテーブルを作成していませんでしたが、「rake db:migrate」を実行する事によりDB上に自動的にテーブルが作成されます。
(5)CRUDの作成
 次もRubyを使うのですが、次の処理に関しては、便利だと思うはずです。先程DBにテーブルを作成しましたが、アプリケーション側(当然プログラム上)から操作できないと話になりません。操作とは、データ作成、読み出し、更新、削除のことです。4つまとめて「CRUD操作」とも呼ぶ操作の事です。Rubyでは、Javaとは違いわざわざコードを記述する必要はありません。コマンド上で「ruby script/generate scaffold クラス名」のように、クラス名にモデル名を記述するだけで「CRUD操作」が自動作成されます。
 
 後は、サーバを立ち上げれば、作成したアプリケーションにアクセスする事ができます。画面は、自動生成で作成されているため、自分でHTMLなどを編集する必要がありますが、ゼロから作成するのが面倒な人や初心者にとっては大変ありがたい機能など思います。ちなみに、モデルでDB上に作成するテーブルの型を指定していましたが、文字列、テキスト、日付などに合わせて画面上に部品を自動生成してくれます。例えば、テキストならHTML上も当然textarea形式で大きさもある程度確保されています。また、日付なら、年、月、日などのようにプルダウンメニューから指定できるようになっています。日付も当然自動的計算されています(例えば、閏年とか)。

 RoRの初歩の説明でしたので、実際にアプリケーションを作成する時には、RoRの良さがより現れてきます。CoC(Convention over Configuration)が実際どうなのかというのもRoRを使用する事により感じ取れると思います。CoCとRoRの関係は、先程のITproさんの記事が参考になります。


 また、Rubyは、「プログラマーに優しい」と聞かれた事があると思いますが、この記事に関しては、以下のURL先が参考になります。この記事もまたITproさんの記事です。



次回は、プログラミングだけではなく、私が実践している効率の事を書きます。