2011年10月31日月曜日

scaffold の中身を見ていく

とあるtweet
Scaffold をそらで写経できない Rails プログラマーは中2からやり直せ。
と、心温まるお言葉が表明されていました。

Ruby の if 文すら怪しい自分は小学生以下決定なわけですが、少しでも学年を上げるべく scaffold の写経をしたいと思います。この前、"rails g scaffold post ..."で作った足場を元に中身を見ていきます。

一通り書いてみて思った感想としては、 RailsGuide の getting started の中身を模写しただけみたいなブログになっちゃいました…。

app/models/post.rb

ソースコード:
class Post < ActiveRecord::Base
end
"rails g scaffold modelname" で作成されるモデルの中身は、ActiveRecord::Base クラスを extend しているだけの空のクラスであることがわかります。

app/helpers/posts_helpers.rb

ソースコード:
module PostsHelper
end
modelname_helpers.rb についてもほぼ同様に空の定義が与えられます(こちらは module ですが)。

config/routes.rb

このファイルは元々存在するファイルですが、generate scaffold で行が追加されます:
resource :posts
# post なんていう紛らわしいモデル名で作ってしまったのは失敗だったかなあ…。

routes.rb では URL → controller のマッピングを行っているようです。
http://guides.rubyonrails.org/routing.html の 2.2節 "CRUD, Verbs, and Actions" を見てみると、"resource :posts" ではデフォルトの7つの挙動が追加されるそうです。
  1. get /posts => index : post の一覧を表示
  2. get /posts/new => new : 新規作成のためのフォームを表示
  3. post /posts => create : 新規作成
  4. get /posts/:id => show : #{:id} 番目の post を表示
  5. get /posts/:id/edit => edit : #{:id} 番目の post を編集するフォームを表示
  6. put /posts/:id => update :  #{:id} 番目の post を更新
  7. delete /posts/:id => destroy :  #{:id} 番目の post を削除
…ちらっと目に入った2.5節の感じだと "resource :posts" と "resource :post"で意味が変わってくるような気がするので、その点は写経的には気をつけるポイントっぽい。
ただ、routes.rb の詳しい内容は上記のガイドだけでもブログ一回分以上の内容がありそうなので別記事にします。

おまけ:rake routes と rails-sh

routes.rb をググってた時、@ITの記事を見つけ、その中で rake routes コマンドと rails-sh を紹介していました。

rake routes コマンドは routes.rb で指定されたマッピングについての情報を表示するためのコマンドです。試しに実行してみたら、先ほど列挙した7つの動作が post について表示されました。resource :posts の効果が確認できた形です。

また、「rails コマンドとか rake コマンドは何かやたらと時間がかかるなあ。仮想マシンなのがいけないのかなあ」と思っていたんですが、@ITの記事を読んでて
以上のように、railsコマンドやrakeはRailsアプリケーションを実装する上で欠かせない便利なツール群です。
しかし、コマンドの実行のたびにRailsアプリケーションをロードするので、毎回少し時間がかかってしまいます。
それを解消するために本記事の筆者の1人である@jugyoが作ったツールが「rails-sh」です。
rails-shは、あらかじめRailsアプリケーションをロードした状態で起動するコマンドラインシェルのようなものです。
という記述を見つけ、時間のかかる理由が納得できました。
rails-sh を使うかどうかは後々考えます…。

app/controllers/posts_controller.rb

おそらく scaffold で生成される Ruby プログラムはここが全てなんだろうと思います。
…こういう書き方をすると、「TDDだから TestUnit の部分を重要視しないなんて、やっぱり初心者だな」と思われるんでしょうか。「メインの関心事(= システムの挙動)に関する部分で」っていう前置きがあれば正しい表現になるのかなあ…。

さて。
class PostsController < ApplicationController
...
end
まず分かることは、ApplicationController のサブクラスであることです。
ApplicationController は ActionController (ActionPack gemsの一部?)を extends しているクラスです。ApplicationController クラスの中身はほぼ空なので、その親クラスである ActionController クラスの中身に意味があるはずです…が、ActionController はちょと巨大なのでブラックボックスにしておきます。
# ほぼ空 = protect_from_forgery のみ = CSRF対策のためのコードが定義されている

PostsContoller クラスでは "resource :posts" で定義される7つの挙動に対応するメソッドが定義されています。
一番シンプルな形のメソッドは
def index
  @posts = Post.all

  respond_to do |format|
    format.html
    format.json { rendor json: @posts }
  end
end
というものです。

最初の @posts 変数への代入では Post クラス(モデルクラス)が使われています。この場合は .all なので、「全ての Post」となります。他のメソッドでの @post の代入では、
  • find メソッドを使って、モデルクラスのデータを検索している
  • new メソッドを使って、モデルクラスのデータを新規作成している
のが見て取れます。

あと、Rubyベストプラクティスを読んでいるおかげか、respond_to が「特別なメソッドを使ってDSLっぽく仕上げる」仕組みなのが想像できます。どうやら actoinpack/lib/action_controller/metal/mime_responds.rb で定義されているようですが、追いきれませんでした…。
exampleを見る限りだと、respond_to に渡されているブロックの引数 format (Webクライアントからのリクエストによって決まるらしい)によってどんな仕事をするのかを記述するようです。
また、(この部分はまったくプログラムを追っていませんが挙動から予想するに)html フォーマットの場合のデフォルトの挙動は methodname.html.erb に処理が移るようです。

その他の scaffold の生成物

きょうのところは勘弁してやる…してください。
      create    db/migrate/20111030125941_create_posts.rb
      invoke    test_unit
      create      test/unit/post_test.rb
      create      test/fixtures/posts.yml
      invoke    erb 
      create      app/views/posts
      create      app/views/posts/index.html.erb
      create      app/views/posts/edit.html.erb
      create      app/views/posts/show.html.erb
      create      app/views/posts/new.html.erb
      create      app/views/posts/_form.html.erb
      invoke    test_unit
      create      test/functional/posts_controller_test.rb
      invoke    helper
      invoke      test_unit
      create        test/unit/helpers/posts_helper_test.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/posts.js.coffee
      invoke    scss
      create      app/assets/stylesheets/posts.css.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.css.scss
大まかに把握しておくと、db の部分、view の部分(erb および assets/coffee,scss)、テストの部分が手つかず。


0 件のコメント:

コメントを投稿