Today, I was building a controller action that was polymorphically finding and associating an object. I could have used a simple dumb solution, such as this:
app/controllers/watches_controller.rb
1 class WatchesController < ApplicationController
2 def create
3watch</span> = <span class="co">Watch</span>.build(<span class="sy">:watcher</span> => current_person) <span class="no"> 4</span> <span class="iv">watch.subject = case
5 when params[:person_id]
6 Person.find(params[:person_id])
7 when params[:event_id]
8 Event.find(params[:event_id])
9 else
10 raise ArgumentError, "Don’t know how to handle other keys… #{params.keys.inspect}"
11 end
12watch</span>.save! <span class="no">13</span> flash[<span class="sy">:notice</span>] = <span class="s"><span class="dl">"</span><span class="k">You're watching </span><span class="il"><span class="idl">#{</span><span class="iv">watch.subject.name}"
14 redirect_to root_url
15 end
16 end
That sucked. Really bad. Why did I have to have a case/switch statement in my controller? Why not use a simpler alternative? I could have gone the full polymorphic route too:
app/controllers/watches_controller.rb
1 class WatchesController < ApplicationController
2 def create
3watch</span> = <span class="co">Watch</span>.build(<span class="sy">:watcher</span> => current_person) <span class="no"> 4</span> <span class="no"> <strong>5</strong></span> <span class="c"># For illustration purposes, this is fine, but INSECURE!!!</span> <span class="no"> 6</span> <span class="iv">watch.subject_type = params[:subject_type]
7watch</span>.subject_id = params[<span class="sy">:subject_id</span>] <span class="no"> 8</span> <span class="iv">watch.save!
9
10 flash[:notice] = "You’re watching #{@watch.subject.name}"
11 redirect_to root_url
12 end
13 end
Then, I remembered that my controller was already a ResourceController implementation. I opened up the code and used this instead:
app/controllers/watches_controller.rb
1 class WatchesController < ResourceController::Base
2 belongs_to :person, :event
3
4 create.before do
5watch</span>.watcher = current_person <span class="no">6</span> <span class="iv">watch.subject = parent_object
7 end
8 end
I needed to add a bit of infrastructure (some routes and has_many declarations in my models), but those were benefits (I know I’ll need them later anyway). My controller code is simpler, and the intention is clearer, I think.
In the same vein, are you following #standup on Twitter?
