jonleighton / conductor

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

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.)

Conductor Plugin

Conductor is a Rails 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 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. You can contact me at j at jonathanleighton dot com.

Overview

Please see the example app for a fully working version of the code below.

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 < Conductor::Base
  def publisher=(publisher_name)
    record.publisher = Publisher.find_by_name(publisher_name)
  end
end

Automatically managing associations

In its simplest form, we can do the following:

class Book < ActiveRecord::Base
  has_many :tags
end

class BookConductor < Conductor::Base
  has_many :tags
end

@book = Book.find(4)
@book.tag_ids # => [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 < ActiveRecord::Base
  has_many :authorships
  has_many :authors, :through => :authorships
end

class BookConductor < Conductor::Base
  has_many :authorships, :require => :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" }
}

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…

Relationship with the Presenter pattern

This started out as an implementation of a conductor as described by New Bamboo in their blog post “Presenters and Conductors on Rails”.

A conductor initially seems similar to a presenter, 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

About

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

License:MIT License


Languages

Language:Ruby 100.0%