conductor icon indicating copy to clipboard operation
conductor copied to clipboard

Conductor is a Rails plugin which helps you manage creating and updating a record and its associations via a single form submission

h2. Status

This is now kind of obsoleted by the accepts_nested_attributes feature built into Active Record. (Though I do hate that they are putting logic for munging HTTP params in the model.)

h1. Conductor Plugin

Conductor is a "Rails":http://rubyonrails.org/ plugin which helps you manage creating and updating a record and its associations via a single form submission. More generally, it is an implementation of the "Unit of Work":http://martinfowler.com/eaaCatalog/unitOfWork.html design pattern.

What it does:

  • Allows complex nested parameters to represent associations
  • Creates, finds or deletes the associated objects as necessary
  • Performs database changes only when told to and within a transaction
  • Aggregates error messages occurring on the objects it manages

This plugin is written by "Jon Leighton":http://jonathanleighton.com/. You can contact me at j at jonathanleighton dot com.

h2. Overview

Please see the "example app":http://github.com/jonleighton/conductor/tree/master/example for a fully working version of the code below.

h3. Manually parsing form data

Conductor can be used as an object-oriented way to parse form data - it gives a place to put code which doesn't really belong in either the controller or the model.

class BookConductor 

h3. Automatically managing associations

In its simplest form, we can do the following:

class Book  [4, 6, 3]

@book_conductor = BookConductor.new(@book)
@book_conductor.tag_ids = [5, 2, 6]

# Neither the database nor the @book object have changed because we haven't saved the conductor:
@book.tag_ids # => [4, 6, 3]

@book_conductor.save # => false - the @book still hasn't changed because something failed, so all changes were rolled back

# ... fix the problem ...

@book_conductor.save # => true
@book.tag_ids # => [5, 2, 6]

We can do more complex things as well, including creating/updating join records on the fly:

class Book  :authorships
end

class BookConductor  :author_id
end

@book_conductor.authorships = {
  # Record doesn't exist (no id field) and won't be created (no author_id field)
  0 => { :role => "" },
  
  # Record is currently associated, but will be removed (no author_id field)
  1 => { :id => 5, :role => "Lead Writer" },
  
  # Record doesn't exist (no id field), but will be created and associated with the book
  2 => { :author_id => 34, :role => "Editor" }
}

h2. Known Problems

Patches welcome!

  • Currently only works with PostgreSQL
  • The documentation sucks. I am rubbish at writing documentation, so it would be really great if some people could write tutorials/blog posts/etc...

h2. Relationship with the Presenter pattern

This started out as an implementation of a conductor as described by "New Bamboo":http://new-bamboo.co.uk/ in their blog post ""Presenters and Conductors on Rails":http://blog.new-bamboo.co.uk/2007/8/31/presenters-conductors-on-rails".

A conductor initially seems similar to a "presenter":http://www.jayfields.com/rails_presenter.htm, particularly as the example Jay Fields gives manages multiple objects in order to keep the controller concise. However, the distinction in my mind is this:

  • A presenter handles view-related state and behaviour in a cleaner and more object-oriented way than helpers
  • A conductor assists the controller in parsing form parameters into one or more objects, and saving them