2010年12月13日月曜日

Ruby on Rails3で出納帳を作ろう・その弍

前回はRails3を使ってアプリケーションを新規に作成(rails new receipt)し、バンドル(bundle)するところまでで終わってしまいました。今回はデータベース(SQLite3を採用)の設計から scaffold という便利な機能を使って CRUD (Create Read Update Delete) のインターフェイスを見る、いじるところまで行くつもりです。

必要になりそうなテーブルは:
  • Receipts: 日付と時刻・月(暗黙的参照: Accounts)・分類(参照: Categories)・概要・詳細(無記入可)・収支
  • Categories: 名前
  • Accounts: 月(ユニーク)・金額
こんなところでしょうか。

流れとしては:
  1. (自動)今月のベースとなる0円のAccounts行を作成。
  2. Categories行をひとつ以上作成。
  3. 収支に応じてReceipts行を作成していく。
  4. (自動)Receiptsをリスト表示するときは、今月分のAccounts行からReceiptsの差分を表示。
  5. (自動)月が変わったらその月のAccounts行を作成し、先月のAccountsおよびReceiptsのsumを保存。
  6. 基本的に2から5を繰り返す。
  7. 先月以前のReceipts行が削除された場合、変化した計算結果をその月以降のAccountsに適用する。
おおむねこんな感じですか。まぁ…はっきり言って雑ですね。RDBMSの特性を真に活かしきれていない気がしますので、「こういう構造のほうが楽ですよ」という意見などあればぜひ教えていただきたいです。

それにしても、こうして書いてみるとデータベースに慣れていない人には何やら難解そうに見えます。ボク自身にも難しそうに見えるというか、よほど集中しないと何が書いてあるのか意味がわかりません。が、Railsでは直感的に作業ができる上にコード量も驚くほど少なく済むので、多分ヒマな人なら一日かからずに作り終えることができるでしょう。実際、この講座に書いてあるのは文章量こそ多かれど実際にタイプするのはその10分の1以下のはずです。

では、まずはデータベースおよびテーブルの作成からですね。





RailsではmigrationやActiveRecordというライブラリがあるのでSQL文をほとんど記述しなくて済みますし、ことRails3においては、新たに導入されたArelというラッパーライブラリがスーパー良い仕事をしてくれるので、SQL文は覚えなくていいと言っても良いと思います。

従って、データベースのコマンドラインで create database receipts; などという左手の指が疲れそうなコマンドを打つ必要はありません。すべてRailsと愉快な仲間達が担当してくれます。

$ cd $(PATH_TO_WORK_DIR)/receipt
$ ls script/
rails*

ls の出力はボクの環境での実行結果ですが、 script/ ディレクトリ以下には rails という実行可能なスクリプトがひとつだけあります。この ./script/rails というスクリプトがデータベースやテーブルを作成する手助けをしてくれます。

それでは早速、簡単そうな Categories テーブルから作成してみましょう。

$ ./script/rails generate scaffold Category name:string
      invoke  active_record
      create    db/migrate/20101213114137_create_categories.rb
      create    app/models/category.rb
      invoke    test_unit
      create      test/unit/category_test.rb
      create      test/fixtures/categories.yml
       route  resources :categories
      invoke  scaffold_controller
      create    app/controllers/categories_controller.rb
      invoke    erb
      create      app/views/categories
      create      app/views/categories/index.html.erb
      create      app/views/categories/edit.html.erb
      create      app/views/categories/show.html.erb
      create      app/views/categories/new.html.erb
      create      app/views/categories/_form.html.erb
      invoke    test_unit
      create      test/functional/categories_controller_test.rb
      invoke    helper
      create      app/helpers/categories_helper.rb
      invoke      test_unit
      create        test/unit/helpers/categories_helper_test.rb
      invoke  stylesheets
      create    public/stylesheets/scaffold.css

めっちゃ出力されますがエラーではありません。「担当のライブラリがこんなファイルを作ったよ」という単なるログの嵐です。慣れです、慣れ。

./script/rails generate というのは「何かを作れ」というコマンドで、続く scaffold というのは文字通り「足場・土台を作れ」という意味になります。つまり端的に言えば、 name(string型) の列を持つ categories というテーブルを作れ、みたいなことになります。 string というのは抽象化された型で、採用したデータベースが MySQL なら varchar(255) に置き換えられます。今回は SQLite3 なので、同様に varchar(255) に置き換えられています。

ちなみに行を扱う上で必須の id 列は暗黙的に作成されているので心配には及びません。また、文字コードはデフォルトで UTF-8 …になっているはずです。Unicodeなのは間違いないです。多分。きっと。

なぜ categories ではなく Category という引数を渡したのか? それはおいおい説明していくとして、このコマンドを実行した時点では、まだデータベースやテーブルは作成されていないのです。

実際にこれをデータベースに適用するには以下のようにします。

$ rake db:migrate
(in /home/sandmark/work/ruby/rails/receipt)
==  CreateCategories: migrating ===============================================
-- create_table(:categories)
   -> 0.0011s
==  CreateCategories: migrated (0.0012s) ======================================


migration はRailsの機構のひとつで、データベース構造に対して柔軟な処理(例えばあとからカラムを追加したり、特定の時点の構造までロールバックしたり)をしてくれます。先程の generatedb/migrate/ ディレクトリに 20101213114137_create_categories.rb というようなファイルが作成されたのですが、これが migration の骨子で、中身はテーブルを作成するための DSL になっています。ファイル名の先頭には日付と時刻が記録されていて、これをもとにロールバックしたり、最小限のクエリでカラムを追加したり、ということが可能になっています。

そしてそんな migration 機構を rake コマンドから呼び出しています。 rake というのはお馴染み GNU の make コマンドの ruby 版で、これからもちょくちょく使っていくことになります。(rake -T とすることで有効なパラメータの一覧が出せますが、ボクが理解しているのは数個だけです)

$ ls db/
development.sqlite3  migrate/  schema.rb  seeds.rb


さて、これで db/development.sqlite3 というデータベースファイルが作成されると同時に、 categories テーブルが作成されました。scaffold の効果はこれだけではありません。

Railsはフレームワークですが、 WEBrick という ruby のライブラリを使って、自身がサーバになる機能も備えています。それを使えばファイルの書き換えなどが即座に反映されるので、いちいちApacheなどのWebサーバを再起動しなくても済み、開発速度が格段に向上します。これは初代からの伝統機能ですが、最近では lighttpd や Mongrel などのサーバを自動検出し、レスポンスの速いサーバになることもできるので、 WEBrick が遅いと感じたら、 Mongrel などをインストールしてみると良いと思います。

$ ./script/rails server
=> Booting WEBrick
=> Rails 3.0.3 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-12-13 21:24:36] INFO  WEBrick 1.3.1
[2010-12-13 21:24:36] INFO  ruby 1.8.7 (2010-06-23) [i686-linux]
[2010-12-13 21:24:41] INFO  WEBrick::HTTPServer#start: pid=5837 port=3000

サーバを起動するにはこのコマンドだけなのです。デフォルトでポート3000で起動するので、早速アクセスしてみましょう。

http://localhost:3000/


こんな画面が表示されるはずです。でもこれは public/index.html を静的に表示しているに過ぎません。

ちょっと http://localhost:3000/categories にアクセスしてみましょう。すると、


こんな画面が出てきました。これは scaffold で自動生成された Category の操作画面(のリスト表示部分)でありまして、いわゆるデータベース用語である CRUD (Create Read Update Delete) のインターフェイスを既にブラウザ上で実現させています。

試しにカテゴリを何か作ってみましょう。 New Category をクリックします。


シンプルで直感的ですね。英語なのが気にかかりますが、日本語には(今のご時世当然といえば当然ですが)対応しているので、Nameの欄に、例えば「食費」と入力して Create Category ボタンをクリックします。すると、


「カテゴリが正常に作成されました」というメッセージと共に、その内容を表示しています。

今回はカラムが Name だけなので表示画面もわびしくさびしいものですが、カラムがどれだけ増えても、また日付や時刻のカラムを含むテーブルを scaffold しても、ちゃんとプルダウンメニュー形式にするなどして対応してくれます。

ここで入力した内容が間違っていたら Edit をクリックして編集し、正しい情報であれば Back をクリックしましょう(今回は Edit をクリックしたときの作業は省略します)。


リスト画面に戻ってきました。先ほど作成した「食費」というカテゴリがリスト形式(と言うにはあまりにもデータが少な過ぎますが)になって表示されています。

好きなカテゴリを作って、 Show をクリックして眺めたり、 Edit で編集したり、 Destroy で削除したりしてみてください。特に Destroy の挙動はぜひとも見ておくべきです(ちなみにサーバの停止は端末内で Ctrl-C です)。

まだまだ「出納帳」には程遠いですが、 ./script/railsmigrationrake、そして scaffold という機能に触れて、ぼんやりとRails開発の輪郭が見えてきたのではないでしょうか。頻繁に使うのは ./script/railsrake なので、そこだけ押さえておけばいい感じです。

とはいえ実際、原始的なデータなら scaffold を使うだけでデータベースとやり取りすることができます。何が凄いって HTML と ruby コードのどちらも一行たりとも書かずに、コマンドラインだけでこれだけのことが実現できてしまうわけですから、これはちょっとしたものです。もっとも、 scaffold はあくまで「土台」に過ぎないので、そんなことするくらいなら表計算ソフトなどを利用したほうがよほど生産的なのは間違いないですね。

さて、次回はいよいよ scaffold が何をしているのか、どういう仕組みで動いているのか、ズバリ「Rails流」に迫ります。「作成→入力→確認→リスト表示」を繰り返しているうちに、あなたは何か大きな不満を抱いたはず。それを解決するのはとても簡単かつ楽しいことですが、今回は紙面の都合でここまでです。

ではまたっ!

0 件のコメント:

コメントを投稿