問題点が分かってきた気がする

うわっ この人も見ているわwww http://d.hatena.ne.jp/lov2much/20090120/1232443661

どうもです。先日はお疲れ様でした。

まとめ

  • なんかわかりあえてきた気がする
  • map.resorcesをネストさせて、それを使うのが好みです。
  • そうするとテンプレートの選択とかがむずい
  • おまけ: 検索はindexアクションで
    • map.resourcesの私なりの解釈は(レシピのはずなのに)Railsレシピブックにいろいろ書きましたので、ご覧いただければと思います。

本題

「ネストする」と「内包する」は私のほうも使い方が曖昧でしたね。すみません。
「内包する」はあくまでCommentはPostの一部であり、Commentそれ自体では存在しえないというかシステムとして直接は扱わないというのを指す感じです。
対して「ネストする」は「集約する」と同じような意味で使っていました。

関連付けした子リソースを、親の一部として扱うのは、rails的ではないのか? というのが、疑問なんですね。
「何の為の親子構造?」とか、「何の為に勝手に親のid振ってくれてるの?」とか、そういう感じですかね。

http://d.hatena.ne.jp/lov2much/20090120/1232443661

なるほど。こういう話ですね。で、私のスタンスはリソースとして親子関係があるならそう扱うのがいいんじゃないか? というスタンスです。id:lov2muchさんとけっこう近い、ですかね?

map.resorcesをネストする件

私がやるとすると、まずルーティングでもネストさせます。

  map.resorces :posts do |post|
    post.resorces :comments 
  end

  # 他にCommentの親が居ればこんな感じで。略記法。
  # map.resorces :photos, {:has_many => :comments }

すると、URLとしては /posts/:post_id/comments とかができます。で、POST /posts/:post_id/comments だとCommentsController#create が呼ばれますよね。このとき、パラメータとして親のIDであるpost_idが取れます。

で、コントローラではid:lov2muchさんがやってるように親を引いて、その関連に対してcreateするのが好きです。

class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id]) # ★1
    @comment = @post.comments.create(params[:comment])
    unless @comment.new_record?
      redirect_to post_path(@post)      # ★2
    else
      render :controller => "posts", :action => "show"  # ★2
    end
  end

ただし。
私や瀧内さんの前提として、ここでは結局のところCommentリソースを作っているので、やっぱりそれに対してCRUDしたいわけです。つまりCommentsControllerで処理したい。というかCommentsControllerでやるのが2.x系のRailsのやりかたじゃないかと思うわけです。賛成なさるかどうかは別として、こうしたいと思っていることはご理解くださいませ。

難しかったところ

さて、そこで問題となるのが上記コードで★をつけたところです。1つ目はPost以外の親を使いたい場合や、(このケースではないと思いますが)Commentを直接作りたい場合です。これをスマートにポリモーフィックにしたい。
★2と★3はこれまた同じ問題で、redirectしたりrenderするテンプレートもネストを意識したものにしたい、と。で★2のredirect_toのほうはpolymorphic_pathというのでできそうなんですが、★3のテンプレートのあたりはむずいね、と。

この★1と★3を何とかするのがプラグインになってれば嬉しいのになー、というのが前のエントリです。ここは私と瀧内さんは別のアプローチをとっているところ。前のエントリは私の考え方ですね。
実際に作ってるアプリでは、ネストがそんなに複雑/大量じゃないので(親-子関係はある程度決まる)、今のところは手で振り分けてます。でも宣言的にできればいいのになーとは常々思ってます。

というあたりで、どのあたりを難しいといっていたのかがご理解いただけるかと。(ゴリゴリ作れば作れるのはまぁ前提として、でももっとキレイにやる方法は)ずーっと悩んでるねー、という感じです。

おまけ

う〜ん、だとすると、CRUDじゃないリクエスト、例えば検索とかは、どうするんだろうという疑問がまあいいや;-p

http://d.hatena.ne.jp/lov2much/20090120/1232443661

つ index

READに関しては、複数件の読み出し(=検索)系のindexと、IDを指定した一件(かつ詳細な場合とか)読み出しのshowがあります。こういった、map.resorcesで作られる(scaffoldにもある)基本的なアクションをどう考えるか、というのは拙著Railsレシピブックにも書きましたのでご覧ください。

また、ある程度固定された検索条件として、ユーザに提供したい集合の読み出し(最近n件とか)は独立したアクションにしてmap.resorcesにも書き、ユーザに対してpublishしたインターフェースとして扱うのもお薦めです。たとえば最近(の閾値は適当に)のPostを提供したい場合は

  # generate /posts/recent
  map.resorces :posts, :collection => {:recent => :get}

こうして

  def recent
    @posts = Post.find(:all, :order => "updated_at DESC", :limit => 20)
    render(:action => "index")
  end

こうするとか。ここでnamed_scopeと繫がってきてnamed_scopeすげー、とかこう書くとRSpecでテストしやすい!! テストによる設計!!! とかそういう世界が広がって、今の考え方に至っています。

Railsレシピブック 183の技

Railsレシピブック 183の技