check_box (w/o _tag) helper adds hidden field to address your problem for you:
<%= check_box 'object', 'boolean_attribute', {}, 'true', 'false' %>
# result:
<input name="object[boolean_attribute]" type="hidden" value="false" />
<input id="object_boolean_attribute" name="object[boolean_attribute]" type="checkbox" value="true" />
UPD: Dealing with nested resources (Product accepts_nested_attributes_for :line_items)
= form_for @product, url: '' do |f|
%p
= f.label :title
= f.text_field :title
= f.fields_for :line_items do |li|
= li.check_box :approved
= li.label :approved, li.object.id
%br
= f.submit
Checking 2 of 3 checkboxes gives me the params as this:
{…, “product”=>{“title”=>”RoR book”, “line_items_attributes”=>{“0″=>{“approved”=>”0”, “id”=>”47”}, “1”=>{“approved”=>”1”, “id”=>”48”}, “2”=>{“approved”=>”1”, “id”=>”51”}}}, “commit”=>”Update Product”, “action”=>”action1”, “controller”=>”test”}
params as YAML for readability:
product: title: RoR book line_items_attributes: '0': approved: '0' id: '47' '1': approved: '1' id: '48' '2': approved: '1' id: '51'
See? No hidden fields but checked/unchecked states are clearly distinguished.
Having this params allows me to use one line of code to update associated line_items:
@product.update_attributes params[:product]
I hope it helps.