解説しよう
ごめんなさい。最初、Rails2.0.2環境で検証してしまってました。
1.2系だと一手間必要になります。
http://d.hatena.ne.jp/actrace/20080314/1205473456
self.schedules << scheduleがなぁ、いまいちわからない。
との事なので、執筆した手前、解説を試みてみます。
といっても、ruby-debugでここを追っかけてみるだけです。
実際に動かしてみると感じをつかみやすいと思います。
確かにここは勉強会の時もわかりづらいという意見が多かったです。
要gem install ruby-debugです。
データの前提条件です。Time.now.beginning_of_monthの条件に当てはまるscheduleレコードが3/15,17,18,22と4件。
nameが'hoge',emailが'fuga'というユーザを作成します。
まずはdebugしたい位置にdebuggerと記述しましょう。
class User < ActiveRecord::Base has_many :user_schedules, :dependent =>:destroy has_many :schedules, :through =>:user_schedules validates_presence_of :name,:email after_create :create_user_schedule def create_user_schedule Schedule.find(:all,:conditions =>["schedule_date >= ?",Time.now.beginning_of_month]).each do |schedule| self.schedules << schedule debugger #<=ここ end end end
続いてサーバーをデバッグモードで立ち上げます(2.0.2)。
% script/server -u
1.2系の場合、config/environment.rbの最後あたりにrequire 'ruby-debug'と追加して、普通にサーバーを起動します。
script/server
で、該当行のプログラムが走る所までブラウザを操作します。この場合はユーザー作成フォームに入力してcreateを押す所までです。すると、createのボタンを押したところで止まります。
この状態で、サーバーのコンソールをみてみましょう。
self.schedules << schedule (rdb:5)
こんな感じの表示になってるはずです。今いる行を確認してみましょう。l=と入力します。
(rdb:5) l= [13, 22] in /Users/yuichi_katahira/svn-work/adjuster/app/models/user.rb 13 has_many :schedules, :through =>:user_schedules 14 validates_presence_of :name,:email 15 after_create :create_user_schedule 16 def create_user_schedule 17 Schedule.find(:all,:conditions =>["schedule_date >= ?",Time.now.beginning_of_month]).each do |schedule| => 18 self.schedules << schedule 19 debugger 20 end 21 end 22 end
18行目にいますね。さて、この時点のselfっていうのはチュートリアルでインスタンス変数だよと説明しています。確認してみましょう。pp selfと入力します。
(rdb:5) pp self #<User id: 23, name: "hoge", email: "fuga">
user_controller.rbのcreateメソッドでcreate,saveされた@userがこの場合のselfのようです。
ではselfにeachで渡しているScheduleのfind(:all)を確認してみましょう。irbと打って、irbの中でSchedule.find(...)と入力し、確認したらexitします。
(rdb:5) irb irb(#<User:0x22b2814>):001:0> Schedule.find(:all,:conditions =>["schedule_date >='2008-03-01'"]) => [#<Schedule id: 1, event_id: 1, schedule_date: "2008-03-15 00:00:00">, #<Schedule id: 2, event_id: 1, schedule_date: "2008-03-22 00:00:00">, #<Schedule id: 3, event_id: 16, schedule_date: "2008-03-17 00:00:00">, #<Schedule id: 4, event_id: 16, schedule_date: "2008-03-18 00:00:00">] irb(#<User:0x22b2814>):002:0>exit
3/15,3/22,3/17,3/18の4レコードですね。
では今eachで渡されているローカル変数scheduleを確認してみましょう。
(rdb:5) pp schedule #<Schedule id: 2, event_id: 1, schedule_date: "2008-03-22 00:00:00">
debuggerを19行目に仕込んでいたので、18行目が処理されすでに2件目の3/22のようです。
(rdb:5) pp self.schedules [#<Schedule id: 1, event_id: 1, schedule_date: "2008-03-15 00:00:00">]
1件目はschedules<<メソッドでselfに関連づけられていますね。
ステップ実行しましょう。
(rdb:5) n Processing UserController#create (for 127.0.0.1 at 2008-03-17 23:06:12) [POST] Session ID: hogehogehogehoge Parameters: {"user"=>{"name"=>"hoge", "email"=>"fuga"}, "commit"=>"Create", "authenticity_token"=>"nantara_hogehoge_fugafugafugafugafuga", "action"=>"create", "controller"=>"user"} User Columns (0.001895) SHOW FIELDS FROM `users` SQL (0.000176) BEGIN User Create (0.000376) INSERT INTO `users` (`name`, `email`) VALUES('hoge', 'fuga') SQL (0.000490) SHOW TABLES Schedule Load (0.000273) SELECT * FROM `schedules` WHERE (schedule_date >= '2008-03-01 00:00:00') Schedule Columns (0.002189) SHOW FIELDS FROM `schedules` UserSchedule Columns (0.002201) SHOW FIELDS FROM `user_schedules` UserSchedule Create (0.000343) INSERT INTO `user_schedules` (`attend`, `schedule_id`, `user_id`) VALUES ('-', 1, 23) Schedule Load (0.000498) SELECT schedules.* FROM schedules INNER JOIN user_schedules ON schedules.id = user_schedules.schedule_id WHERE ((user_schedules.user_id = 23)) Schedule Load (0.011225) SELECT * FROM `schedules` WHERE (schedule_date >='2008-03-01') CACHE (0.000000) SELECT * FROM `schedules` WHERE (schedule_date >='2008-03-01')
SQLが発行されましたね。
a.UserをINSERT
b.Scheduleを条件に従ってSELECT
c.user_scheduleをINSERT
ちなみに余計なSQLは、変数を参照したりした為に発行されたものです。
ブレイクポイントまで飛んでみましょう。変数を確認。
(rdb:5) c (rdb:5) pp self #<User id: 23, name: "hoge", email: "fuga"> (rdb:5) pp self.schedules [#<Schedule id: 1, event_id: 1, schedule_date: "2008-03-15 00:00:00">, #<Schedule id: 2, event_id: 1, schedule_date: "2008-03-22 00:00:00">] (rdb:5) pp schedule #<Schedule id: 3, event_id: 16, schedule_date: "2008-03-17 00:00:00">
後は終わるまで繰り返しです。どうでしょう。なんとなく感じはつかめたでしょうか?
あ、schedules<<を説明していませんでした。
これはRailsのAPIでActiveRecord::Associations::ClassMethodsのhas_manyを見ると良いでしょう。has_manyを定義すると、自動で定義されるメソッド中のひとつです。
関連名(この場合schedules)<< までで一つのメソッドです。関連オブジェクトを自分の所有物に追加します。(自動的にDBへ保存されます)。
なお、Associationsについてはこちらが詳しいです。
http://wota.jp/ac/?date=20060120