Archive for October, 2007

Sub List Rails Plugin

Friday, October 12th, 2007
My fixes to this plugin have been superseded by a plugin I wrote from scratch. I highly recommend you check it out.

The Sub List Rails plugin will make your coding experience 10 time easier. It’s a simple way to CRUD children objects within a parent form. For example, let’s say you’re building a system to track a basketball league. You have two tables among others called Teams, and Players. Each Team has_many :players. Using standard Rails scaffolding you will have CRUD screens to modify each team, but you won’t have a convenient way to add players to a team. Using the Sub List plugin you provide a _player partial, add a few calls to the parent’s controller, add a few calls to your views, bada boom bada bing, your done – “instant” AJAX’ed parent/child forms.

I found a few bugs with the current version. I contacted Luke but have not heard back so here’s an archived copy with quite a few fixes. One of the fixes requires sessions enabled.

Sub List Rails Plugin With My Modifications

You can find more info, and the original release at http://rorsublist.rubyforge.org/

There’s also a handy tutorial available: http://cottee.org/articles/2006/06/30/ror-sublist-plugin (I’m getting a 500 error right now, but I’ve seen the page online before).

Expand the code below to see an example controller using the SubList plugin. You will find that the modifications required to the base scaffold code is very minimal. This controller is fully functional, so you can refer to this code if you hit a wall (there’s a few subtleties to trip on). Note that the last method in the code below is a hack and you may need to add it to your controller should you have problems deleting your children objects.

BTW, I’m running with Ruby On Rails 2 Pre-Release.

class InvoicesController < ApplicationController
  include UIEnhancements::SubList
  helper :SubList  

  sub_list 'Purchase', 'invoice' do |new_purchase|

  end

  # GET /invoices
  # GET /invoices.xml
  def index
    @invoices = Invoice.find(:all)

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @invoices }
    end
  end

  # GET /invoices/1
  # GET /invoices/1.xml
  def show
    @invoice = Invoice.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.xml  { render :xml => @invoice }
    end
  end

  # GET /invoices/new
  # GET /invoices/new.xml
  def new
    @invoice = Invoice.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @invoice }
    end
  end

  # GET /invoices/1/edit
  def edit
    @invoice = Invoice.find(params[:id])
    initialize_purchases
    prepare_purchases
  end

  # POST /invoices
  # POST /invoices.xml
  def create
    @invoice = Invoice.new(params[:invoice])

    respond_to do |format|
      if initialize_purchases &#038;&#038; @invoice.save
        flash[:notice] = 'Invoice was successfully created.'
        format.html { redirect_to(@invoice) }
        format.xml  { render :xml => @invoice, :status => :created, :location => @invoice }
      else
        format.html { prepare_purchases; render :action => "new" }
        format.xml  { render :xml => @invoice.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /invoices/1
  # PUT /invoices/1.xml
  def update
    @invoice = Invoice.find(params[:id])

    respond_to do |format|
      if initialize_purchases &#038;&#038; @invoice.update_attributes(params[:invoice])
        flash[:notice] = 'Invoice was successfully updated.'
        format.html { redirect_to(@invoice) }
        format.xml  { head :ok }
      else
        format.html { prepare_purchases; render :action => "edit" }
        format.xml  { render :xml => @invoice.errors, :status => :unprocessable_entity }
      end
    end
  end

  # DELETE /invoices/1
  # DELETE /invoices/1.xml
  def destroy
    @invoice = Invoice.find(params[:id])
    @invoice.destroy

    respond_to do |format|
      format.html { redirect_to(invoices_url) }
      format.xml  { head :ok }
    end
  end

  # FIXME This is meant to be auto inserted by sublist
  def remove_purchase
    obj = find_purchase(params[:id])
    if ! obj.nil?
      obj.destroy
    end
    render :text => ''
  end

end