class Drug < ActiveRecord::Base
has_many :indications
has_many :diseases, :through => :indications, :uniq => true
Here a drug can be used to treat multiple diseases and any disease can be treated with multiple drugs (that side of the association is not shown). The 'indications' is a basic linking table with drug_id and disease_id.In the drug#new and drug#edit forms you might use a select menu that allows you to select multiple diseases. I use the 'simple_form' gem for my forms and the 'association' method makes this trivial.
<%= f.association :diseases,
:collection => Disease.all(:order => 'name') %>
In order for this to work you need to add an attr_accessible called :disease_ids to your model. With that, simple form should handle all details needed to create and update the association.But there is a problem with the edit/update actions when you want to deselect ALL diseases. If you do this in the form then no disease_ids parameter will get passed to your controller and so this column will not get updated. It is a classic issue with HTML form updates and applies to checkboxes as well.
The solution is to add a line to the updater action in your controller that sets the disease_ids parameter to an empty array if it does not exist:
def update
params[:drug][:disease_ids] ||= []
@drug = Drug.find(params[:id])
[...]
This works fine - adds a bit of clutter to the controller but there you go...However, this will break a basic functional test for the update action and you will get an error similar to this:
test_update_valid(DrugsControllerTest):
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
The problem stems from the basic stub for your object not passing a params[:drug] hash. Now, I'm not an expert on stubs/mocking so there may be a much cleaner way of fixing this, but I fix this by explicitly creating the needed parameters and passing them in the 'put' method.Here is an example of a basic update test
def test_update_valid
Drug.any_instance.stubs(:valid?).returns(true)
put :update, :id => Drug.first
assert_redirected_to drug_url(assigns(:drug))
end
and here is the modified one that will work def test_update_valid
Drug.any_instance.stubs(:valid?).returns(true)
put :update, { :id => Drug.first, :drug => { :disease_ids => [] } }
assert_redirected_to drug_url(assigns(:drug))
end
Of course I should be building out the tests to provide truly useful tests, but if you can't get beyond this step, the others don't really matter.
Hope this helps....
No comments:
Post a Comment