mongoid/mongoid — Issue #1553: HABTM regression. Editing HABTM relations
Author: tmaier
Created: 2012-01-04 23:10:53+00:00
State: open

Hi there,

I'm just testing my specs with the latest mongoid as of 42dc49365d7f2ecfc43e33ef341388aadf99fdab

The error happens when editing a habtm relation. It's quite inconsistent, so I created a more complex test case.

With mongoid 2.3.4, I get the following results:

```terminal
GET /projects/1/work_breakdown_structures/2/edit
edit
tasks true, false, false
tasks false, true, false
tasks false, false, true
tasks true, true, false
tasks true, false, true
tasks false, true, true
tasks true, true, true
```

After updating to 42dc49365d7f2ecfc43e33ef341388aadf99fdab

```terminal
GET /projects/1/work_breakdown_structures/2/edit
edit
tasks true, false, false
tasks false, true, false
tasks false, false, true
tasks true, true, false
tasks true, false, true (FAILED - 1)
tasks false, true, true (FAILED - 2)
tasks true, true, true

Failures:

1) WorkBreakdownStructures GET /projects/1/work_breakdown_structures/2/edit edit tasks true, false, true
Failure/Error: page.should have_link task1.to_s, href: project_task_path(project, task1)
expected link "Garten bepflanzen" to return something
# ./spec/requests/work_breakdown_structures_spec.rb:425:in `block (7 levels) in '
# ./spec/requests/work_breakdown_structures_spec.rb:423:in `block (6 levels) in '
# ./spec/requests/work_breakdown_structures_spec.rb:420:in `block (5 levels) in '

2) WorkBreakdownStructures GET /projects/1/work_breakdown_structures/2/edit edit tasks false, true, true
Failure/Error: page.should have_link task2.to_s, href: project_task_path(project, task2)
expected link "Decke betonieren" to return something
# ./spec/requests/work_breakdown_structures_spec.rb:431:in `block (7 levels) in '
# ./spec/requests/work_breakdown_structures_spec.rb:423:in `block (6 levels) in '
# ./spec/requests/work_breakdown_structures_spec.rb:420:in `block (5 levels) in '

Finished in 36.96 seconds
25 examples, 2 failures
```

## The model

```ruby
class WorkBreakdownStructure
include Mongoid::Document
include Mongoid::Taggable

include Mongoid::Tree

# @return [Array] list of related activities
has_and_belongs_to_many :tasks, :class_name => "Task", :inverse_of => :work_breakdown_structures

attr_accessible :description, :project_id, :responsible_id, :parent_id, :task_ids, :tags
attr_accessor :accessible

# @return [String]
def to_s
title
end

private
def mass_assignment_authorizer(role = :default)
super + (accessible || [])
end
end
```

```ruby
class Task
include Mongoid::Document

# @return [Array] related work breakdown structure nodes
has_and_belongs_to_many :work_breakdown_structures, :class_name => "WorkBreakdownStructure", :inverse_of => :tasks

attr_accessible :activity_identifier, :title, :project_id, :target_attributes, :actual_attributes, :work_breakdown_structure_ids

# @return [String]
def to_s
title
end
end
```

## Controller / View

Inherited Resources, simple_form

Nothing special

```haml
= f.input :task_ids, as: :check_boxes, collection: task_items
```

## The request spec

```ruby
describe "GET /projects/1/work_breakdown_structures/2/edit" do
let!(:person) {Fabricate(:person, :organization => project.owner)}
let!(:task1) {Fabricate(:task, :project => project, :title => "Garten bepflanzen")}
let!(:task2) {Fabricate(:task, :project => project, :title => "Decke betonieren")}
let!(:task3) {Fabricate(:task, :project => project, :title => "Wand einschalen")}
let!(:wbs1) {Fabricate(:work_breakdown_structure, project: project, title: "Test-WBS 01", description: "This is the test description", tags: 'mega type', responsible: user, tasks: [task1, task2])}
let!(:wbs_sibling1) {Fabricate(:work_breakdown_structure, project: project, parent: wbs1)}
let!(:wbs_sibling2) {Fabricate(:work_breakdown_structure, project: project, parent: wbs1)}
let!(:wbs2) {Fabricate(:work_breakdown_structure, project: project)}

describe "edit" do
before(:each) do
visit edit_project_work_breakdown_structure_path(project, wbs1)
end

[ [true, false, false], [false, true, false], [false, false, true], [true, true, false], [true, false, true], [false, true, true], [true, true, true]].each do |t1, t2, t3|
it "tasks #{t1}, #{t2}, #{t3}" do
within(".content") do
within_fieldset("Related task elements") do
page.should have_selector "select#work_breakdown_structure_responsible_id", value: user.id, text: user.to_s
page.should have_content task1
page.should have_content task2
page.should have_content task3

t1 ? check(task1.to_s) : uncheck(task1.to_s)
t2 ? check(task2.to_s) : uncheck(task2.to_s)
t3 ? check(task3.to_s) : uncheck(task3.to_s)
end

click_button "Update Work breakdown structure"
end

within(".content") do
page.should have_content "Work breakdown structure was successfully updated."

within(".related_activities") do
if t1
page.should have_link task1.to_s, href: project_task_path(project, task1)
else
page.should_not have_link task1.to_s, href: project_task_path(project, task1)
end

if t2
page.should have_link task2.to_s, href: project_task_path(project, task2)
else
page.should_not have_link task2.to_s, href: project_task_path(project, task2)
end

if t3
page.should have_link task3.to_s, href: project_task_path(project, task3)
else
page.should_not have_link task3.to_s, href: project_task_path(project, task3)
end

#click_link task3.to_s
end
end

#within(".content") do
# page.should have_link wbs1.to_s, href: project_work_breakdown_structure_path(project, wbs1)
#end
end
end
end
end
```

If you need more/other data I'm happy to provide them for you.