Scaffolding for auto complete on a complex, nested form

I just updated View Mapper to work with my fork of the Rails auto_complete plugin that allows for repeated text fields on the same complex form. This means that View Mapper can now generate scaffolding code that uses both nested attributes and the auto_complete plugin at the same time, to display a form like this:

To generate this sort of complex form for two of your models you’ll first need to install my “repeated_auto_complete” gem from gemcutter.org:

$ gem sources -a http://gemcutter.org
http://gemcutter.org added to sources
$ sudo gem install repeated_auto_complete
Successfully installed repeated_auto_complete-0.1.0
1 gem installed
Installing ri documentation for repeated_auto_complete-0.1.0...
Installing RDoc documentation for repeated_auto_complete-0.1.0...

To learn more about repeated_auto_complete and what it does, see: http://patshaughnessy.net/repeated_auto_complete. Now you can generate a complex form like the one shown above for two of your models in a has_many/belongs_to, has_and_belongs_to_many or has_many, :through association by installing View Mapper (version 0.3.1 or later):

$ sudo gem install view_mapper
Successfully installed view_mapper-0.3.1
1 gem installed
Installing ri documentation for view_mapper-0.3.1...
Installing RDoc documentation for view_mapper-0.3.1...

… and then running the “view_for” generator with a view option called “has_many_auto_complete,” like this:

./script/generate view_for group --view has_many_auto_complete:people

 

Detailed Example

To see how easy it is to create a complex form using View Mapper, let’s create one from scratch in a brand new Rails app. You should be able to follow along using the commands below on your machine. First, let’s create a new Rails application:

$ rails complex_auto_complete
      create  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      create  config/environments
      create  config/initializers
      create  config/locales
    … etc..
      create  log/server.log
      create  log/production.log
      create  log/development.log
      create  log/test.log

The first thing I’ll do is install the auto_complete plugin. However, since I’m planning to use auto_complete on a complex form, I’ll need to get my fork of auto_complete which I’ve deployed as a gem on gemcutter.org:

$ gem sources -a http://gemcutter.org
http://gemcutter.org added to sources
$ sudo gem install repeated_auto_complete
Successfully installed repeated_auto_complete-0.1.0
1 gem installed
Installing ri documentation for repeated_auto_complete-0.1.0...
Installing RDoc documentation for repeated_auto_complete-0.1.0...

And let’s update my new app to use the repeated_auto_complete gem by editing the config/environment.rb file:

Rails::Initializer.run do |config|
…etc…
  config.gem "repeated_auto_complete"
…etc…

If you prefer, you can also install this the old fashioned way, using “script/plugin install git://github.com/patshaughnessy/auto_complete.git”. Next, let’s generate a new model called “person” with a couple of fields for name and age, like the ones shown above in the screen shot:

$ cd complex_auto_complete/
$ ./script/generate model person name:string age:integer group_id:integer
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/person.rb
      create  test/unit/person_test.rb
      create  test/fixtures/people.yml
      create  db/migrate
      create  db/migrate/20091125195040_create_people.rb

Note that I’ve also included an integer field for the group id, since in a minute I’ll be adding a belongs_to association for people to groups.

Now I’m ready to use View Mapper… if you haven’t installed that yet, get it from gemcutter.org like this:

$ sudo gem install view_mapper
Successfully installed view_mapper-0.3.1
1 gem installed
Installing ri documentation for view_mapper-0.3.1...
Installing RDoc documentation for view_mapper-0.3.1...

You’ll need at least version 0.3.1 to use auto_complete on a complex form. Now I can use View Mapper to create scaffolding for a new “group” model that has many people with auto_complete like this:

$ ./script/generate scaffold_for_view group name:string
                    --view has_many_auto_complete:people
       error  Table for model 'person' does not exist
              - run rake db:migrate first.

Yes… I forgot to create the people table in my database; if we do that:

$ rake db:migrate
(in /Users/pat/rails-apps/complex_auto_complete)
==  CreatePeople: migrating ===================================================
-- create_table(:people)
   -> 0.0014s
==  CreatePeople: migrated (0.0015s) ==========================================

… and then re-run View Mapper:

$ ./script/generate scaffold_for_view group name:string
                    --view has_many_auto_complete:people
     warning  Model Person does not contain a belongs_to
              association for Group.

… we get a second error message! This time View Mapper is reminding me that I still need to add “belongs_to :group” to the person model in order to get the complex form to work. Let’s do that now:

class Person < ActiveRecord::Base
  belongs_to :group
end

And now I can run View Mapper once more:

$ ./script/generate scaffold_for_view group name:string
                    --view has_many_auto_complete:people
      exists  app/models/
…etc…
      create  app/models/group.rb
      create  test/unit/group_test.rb
      create  test/fixtures/groups.yml
      exists  db/migrate
      create  db/migrate/20091125195715_create_groups.rb
      create  app/views/groups/show.html.erb
      create  app/views/groups/_form.html.erb
      create  app/views/groups/_person.html.erb
      create  public/javascripts/nested_attributes.js
       route  map.connect 'auto_complete_for_group_name',
                          :controller => 'groups',
                          :action => 'auto_complete_for_group_name'
       route  map.connect 'auto_complete_for_person_name',
                          :controller => 'groups',
                          :action => 'auto_complete_for_person_name'
       route  map.connect 'auto_complete_for_person_age',
                          :controller => 'groups',
                          :action => 'auto_complete_for_person_age'

Now you can see the new scaffolding files View Mapper created, including some new scaffolding files peculiar to complex forms, like “nested_attributes.js,” “_form.html.erb,” and “_person.html.erb.” You may also have noticed View Mapper added three new routes related to the auto_complete plugin; these will handle the AJAX requests used to return the auto_complete options to the form.

Now to get it all to work, I just need to create the group table:

$ rake db:migrate
(in /Users/pat/rails-apps/complex_auto_complete)
==  CreateGroups: migrating ===================================================
-- create_table(:groups)
   -> 0.0013s
==  CreateGroups: migrated (0.0014s) ==========================================


Now running my server and creating a new group I see:

If you click “Add a Person” you’ll see nested fields for new Person records appear. This all works exactly the same way as the standard nested attributes scaffolding that I described in my last post. The only difference is that in this form, each of the text fields present in both the parent (“Group”) and child (“Person”) models are displayed using the “text_field_with_auto_complete” method.

I’ll try to write up a detailed walk through of how this scaffolding actually works as soon as I can… there are a lot of interesting details in the code that will be fun to look at. In the meantime, hopefully this scaffolding will make it easier for you to learn how to use auto_complete and nested attributes together in your app.