Pat Shaughnessy

Ribadesella, Spain

Paperclip scaffolding

October 16, 2009 · 2 comments

I just updated the View Mapper gem to support Paperclip. You can use it to generate scaffolding code that supports uploading and downloading Paperclip file attachments.

Creating a view for an existing model

If you have a model like this:

class Song < ActiveRecord::Base
  has_attached_file :mp3
end

… you can generate a “Paperclip view” for this model like this:

script/generate view_for song --view paperclip

This will generate a controller, view and other code files that support uploading and downloading files. If you run your app you’ll see the typical scaffolding user interface but with a file field for the “mp3” Paperclip attachment:

View Mapper has:

  • inspected your model (“Song” in this example) and found its Paperclip attachments and other standard ActiveRecord attributes
  • called the Rails scaffold generator and passed in the ActiveRecord columns it found
  • added additional view code to support Paperclip (e.g. set the form to use multipart/form-data encoding)
  • created a file field in the form for each Paperclip attachment (“mp3” in this example), as well as a link to each attachment in the show view code file.

If you’re not very familiar with Paperclip and how to use it or if you just want to get a Rails upload form working very quickly, then View Mapper can help you.

Creating an entirely new Paperclip model and view

View Mapper also provides a generator called “scaffold_for_view” that is identical to the standard Rails scaffold generator, except it will create the specified view. As an example, let’s create a new Rails app from scratch that uses Paperclip; you should be able to type in these precise commands on your machine and get this example to work.

First, let’s install View Mapper and create a new Rails app to display my MP3 library online (ignoring copyright issues for now):

$ gem sources -a http://gemcutter.org
http://gemcutter.org added to sources
$ sudo gem install view_mapper
Successfully installed view_mapper-0.2.0
1 gem installed
Installing ri documentation for view_mapper-0.2.0...
Installing RDoc documentation for view_mapper-0.2.0...
$ rails music
      create  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
etc…

And now we can generate a new “Song” model that has a Paperclip attachment called “MP3” using View Mapper like this:

$ cd music
$ ./script/generate scaffold_for_view song name:string artist:string
                    album:string play_count:integer --view paperclip:mp3
       error  The Paperclip plugin does not appear to be installed.

Wait… I forgot to install Paperclip; let’s do that and then try again:

$ ./script/plugin install git://github.com/thoughtbot/paperclip.git
Initialized empty Git repository in /Users/pat/rails-apps/music/vendor/plugins/paperclip/.git/
remote: Counting objects: 71, done.
remote: Compressing objects: 100% (59/59), done.
remote: Total 71 (delta 7), reused 29 (delta 3)
Unpacking objects: 100% (71/71), done.
From git://github.com/thoughtbot/paperclip
 * branch            HEAD       -> FETCH_HEAD
$ ./script/generate scaffold_for_view song name:string artist:string
                    album:string play_count:integer --view paperclip:mp3
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/songs
      exists  app/views/layouts/
      exists  test/functional/
etc…

Finally, we just need to run db:migrate – one minor detail here is that the scaffold_for_view generator included the Paperclip columns (“mp3_file_name,” “mp3_content_type,” etc…) in the migration file to create the songs table:

$ rake db:migrate
(in /Users/pat/rails-apps/music)
==  CreateSongs: migrating ====================================================
-- create_table(:songs)
   -> 0.0022s
==  CreateSongs: migrated (0.0024s) ===========================================

Now you can run your app and see the scaffolding UI I showed above, and will be able to upload and download MP3 files using Paperclip.

Let’s take a quick look at exactly what is different about the scaffolding code View Mapper generated vs. the standard Rails scaffolding code:

<h1>New song</h1>

<% form_for(@song, :html => { :multipart => true }) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>

… etc …

  <p>
    <%= f.label :mp3 %><br />
    <%= f.file_field :mp3 %>
  </p>
  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', songs_path %>

The code in bold was generated by View Mapper specifically to support Paperclip since we used the “--view paperclip” command line option. You can see that “:html => { :multipart => :true }” was added to form_for to allow for file uploads, and also a file_field was added for the mp3 Paperclip attachment.

If you take a look at the show view, you’ll see:

<p>
  <b>Name:</b>
  <%=h @song.name %>
</p>

… etc …

<p>
  <b>Mp3:</b>
  <%= link_to @song.mp3_file_name, @song.mp3.url %><br>
</p>

<%= link_to 'Edit', edit_song_path(@song) %> |
<%= link_to 'Back', songs_path %>

Here a link to the file attachment was added, using Paperclip to provide the name and URL of the attachment.

Next I’ll be adding support for nested attributes and complex forms to View Mapper.

2 comments Tags:··

View Mapper: Scaffolding for your models and plugins

October 01, 2009 · 7 comments

View Mapper will generate scaffolding illustrating how to write view code using a specified plugin or feature with your existing models. It can also generate new models.

A couple simple examples:

script/generate view_for office --view auto_complete:address

… will generate Rails scaffolding code that displays a form for an existing “office” model, with auto complete on the “address” field.

script/generate scaffold_for_view office address:string code:string
                                  --view auto_complete:address

… will generate the same form, but also create the “office” model class file, a migration file containing the “address” and “code” columns, and other standard scaffolding files as well.

The idea behind View Mapper is that it’s easy to write simple, concise model classes representing your domain objects using ActiveRecord, but very hard to implement the corresponding views using a combination of HTML, Javascript Rails helper functions, routes, controllers, etc. If you’re not very familiar with a certain plugin you want to use in your app, View Mapper can help you get started in the right direction by generating a working example with scaffolding code.

If you’re developing a Rails plugin or gem it’s easy to write your own View Mapper module for your plugin’s users to call with View Mapper.

Code:   http://github.com/patshaughnessy/view_mapper

Install:

sudo gem install view_mapper

Usage:

Two generators are provided, called view_for and scaffold_for_view:

script/generate view_for model [ --view view_name:param ]

This will generate the specified view code for an existing model. The view_for generator will look for your model, inspect all of its columns and then generate standard Rails scaffolding containing a form field for each existing column.

If you also specify a view, then a custom view will be created using the specified Rails feature or plugin, using the specified parameter.

script/generate scaffold_for_view model attributes [ --view view_name:param ]

If you don’t specify a view, then this command is identical to the standard Rails scaffold generator.

If you do specify a view, then the entire working set of a model, views and controller will be generated to implement the specified Rails feature or plugin, using the specified parameter.

Views:

Right now, I’ve implemented eight views:

  • auto_complete: Uses the standard Rails auto_complete plugin to implement type ahead behavior for the specified field.
  • paperclip: Uses the Paperclip plugin to upload and download file attachments.
  • has_many: Displays a complex form to edit two or more associated models.
  • has_many_auto_complete: This is the same as has_many but also uses the auto_complete plugin to implement type ahead behavior for each text field. This view requires you to install my fork of the Rails auto_complete plugin.
  • belongs_to: Generates scaffolding that allows you to select an existing, associated model.
  • belongs_to_auto_complete: Generates scaffolding that allows you to select an existing, associated model using auto_complete.
  • has_many_existing: Generates scaffolding for a complex form to edit two models that have a has_many, :through association with a third model. Use this if you have a many-many relationship with existing data.
  • (Default) If no view is specified, then standard Rails scaffold code will be generated.

I’ll be implementing more views in the coming weeks and months. There is also an API for implementing your own View Mapper module, for example to generate code illustrating how to use a plugin or gem you are working on. In the future I’ll document this as well.

7 comments Tags:·

Auto_complete scaffolding

October 01, 2009 · 0 comments

I’ve written a lot here about the Rails auto_complete plugin; I’ve also refactored the auto_complete plugin to support repeated fields and named scopes. Today I’d like to show how you can automatically generate Rails view and controller code with auto_complete behavior for one of your models using a new gem I’ve written called View Mapper. If you’ve never used the auto_complete plugin before this is a great way to learn quickly how to use it in your app; even if you are familiar with the plugin using scaffolding like this can help to get a working auto_complete form up and running quickly and let you concentrate on more important parts of your app.

Let’s say you have an existing model in your app called “Person:”

Class Person < ActiveRecord::Base
end

And suppose the Person model has two string attributes for the person’s name and the name of the office they work in:

class CreatePeople < ActiveRecord::Migration
  def self.up
    create_table :people do |t|
      t.string :name
      t.string :office
      etc…

Now let’s install View Mapper so we can generate an auto_complete view for our person model. Since I’ve only deployed view_mapper on gemcutter.org for now, you’ll also need to add gemcutter as a gem source if you haven’t already.

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

Now with view_mapper you can run a single command to generate scaffolding that displays the your existing person model’s fields in a form with auto_complete type ahead behavior for the office field:

$ ./script/generate view_for person --view auto_complete:office
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/people
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  test/unit/helpers/
      exists  public/stylesheets/
      create  app/views/people/index.html.erb
      create  app/views/people/show.html.erb
      create  app/views/people/new.html.erb
      create  app/views/people/edit.html.erb
      create  app/views/layouts/people.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/people_controller.rb
      create  test/functional/people_controller_test.rb
      create  app/helpers/people_helper.rb
      create  test/unit/helpers/people_helper_test.rb
       route  map.resources :people
       route  map.connect 'auto_complete_for_person_office',
                          :controller => 'people',
                          :action => 'auto_complete_for_person_office'

This works just like the Rails scaffold generator, except that the view_for generator has also:

  • inspected your person model and found the name and office columns.
  • added a route for “auto_complete_for_person_office” to routes.rb.
  • added a call to “auto_complete_for :person, :office” to PersonController.
  • used text_field_for_auto_complete on the office field in your new and edit forms.
  • inserted “javascript_include_tag :defaults” into views/layouts/people.html.erb to load the prototype javascript library.

If you start up your application and create a few person records with names and addresses, then you will see the auto_complete plugin working!

With this working example right inside your application, you can easily review exactly how the view, route and controller files use auto_complete. After that you can adapt the view to fit into your application’s design and delete the scaffolding you don’t really need or want.

As another example, let’s create an entirely new Rails application completely from scratch, and use View Mapper to setup auto_complete inside it:

$ rails auto_complete_example
      create  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      etc…
$ cd auto_complete_example

First, let’s install the auto_complete plugin. (In a future post I’ll show how to use View Mapper with my fork of auto_complete in a complex form.)

$ ./script/plugin install git://github.com/rails/auto_complete.git
Initialized empty Git repository in /Users/pat/rails-apps/auto_complete_example/vendor/plugins/auto_complete/.git/
remote: Counting objects: 13, done.
remote: Compressing objects: 100% (12/12), done.
remote: Total 13 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (13/13), done.
From git://github.com/rails/auto_complete
 * branch            HEAD       -> FETCH_HEAD

Now that we have the plugin installed, let’s create our scaffolding. Along with the “view_for” generator I used above, View Mapper also provides a generator called “scaffold_for_view.” This works the same way, except it just creates a new model the same way the Rails scaffold generator does, instead of inspecting an existing model.

Let’s create the same person model we used above, and an auto_complete view:

$ ./script/generate scaffold_for_view person name:string office:string
                                      --view auto_complete:office
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/people
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  test/unit/helpers/
      exists  public/stylesheets/
      create  app/views/people/index.html.erb
      create  app/views/people/show.html.erb
      create  app/views/people/new.html.erb
      create  app/views/people/edit.html.erb
      create  app/views/layouts/people.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/people_controller.rb
      create  test/functional/people_controller_test.rb
      create  app/helpers/people_helper.rb
      create  test/unit/helpers/people_helper_test.rb
       route  map.resources :people
  dependency  model
      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/20091001161349_create_people.rb
       route  map.connect 'auto_complete_for_person_office',
                          :controller => 'people',
                          :action =>'auto_complete_for_person_office'

Note the syntax is the same as the standard Rails scaffold generator, except I’ve added the “view” parameter to specify we want the auto_complete plugin to be used in the view.

Now we just need to migrate the database schema and run our app:

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

And if you add a few records, you’ll see auto_complete working!

I’ll be adding more views to View Mapper soon, and in future posts here I’ll write about how to generate scaffolding for Paperclip file attachments, my version of auto_complete used on a complex form, and also how to write your own views for View Mapper.

0 comments Tags:··