views:

540

answers:

2

Alright, I need some advice and best practices from some Rails people.

I'm fairly new to the platform but I've done database-backed web development work before in Java. At this point I've worked through about twenty tutorials, and I keep getting obstructed at the same place.

Here's what I've done and what I'm experiencing. I've created a new application using

rails my_rails_app

I've created my schema and tables in MySQL, and I've created an account for rails (rails_root/pass1234) and updated config/databases.yml to reflect the new account:

development:
    adapter: mysql
    encoding: utf8
    reconnect: false
    database: demo_development
    username: rails_root
    password: pass1234
    host: localhost

At this point, I generate the scaffold for the 'customers' table:

ruby script/generate scaffold customer

Which returns successfully. So now we start the server:

ruby script/server

Which boots Mongrel and starts the server up as expected at port 3000 on the local machine. After directing the browser to http://localhost:3000/customers, the page correctly says "Listing customers" but has no fields listed. When I press the link for "new customer" there are still no fields to modify, but I am allowed to create an empty customer record. Since this operation places 'nulls' in all fields (some of which aren't nullable,) a Mysql error is thrown -- "column 'name' cannot be null" with the SQL query it was attempting to run.

I'm confused mainly because if it's picking up my fields at that point, why is it not displaying them earlier? Clearly it's connecting to the right database and has identified the correct table. What's the deal?

Now, on the other hand, if I place entries into the table prior to starting the server, the page now shows 'Show,' 'Edit,' and 'Destroy' link options below the "Listing customers" header. However, no fields or data are reflected. If I click "Destroy," the record in the table is indeed destroyed. But I still haven't seen any of the records or fields on a web page generated by rails unless it was an error message.

This was the output from my generate scaffold command:

\ROR\my_app>ruby script/generate scaffold contact
      exists  app/models/
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/contacts
      exists  app/views/layouts/
      exists  test/functional/
      exists  test/unit/
      create  test/unit/helpers/
      exists  public/stylesheets/
      create  app/views/contacts/index.html.erb
      create  app/views/contacts/show.html.erb
      create  app/views/contacts/new.html.erb
      create  app/views/contacts/edit.html.erb
      create  app/views/layouts/contacts.html.erb
      create  public/stylesheets/scaffold.css
      create  app/controllers/contacts_controller.rb
      create  test/functional/contacts_controller_test.rb
      create  app/helpers/contacts_helper.rb
      create  test/unit/helpers/contacts_helper_test.rb
       route  map.resources :contacts
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/contact.rb
      create    test/unit/contact_test.rb
      create    test/fixtures/contacts.yml
      create    db/migrate
      create    db/migrate/20090414185634_create_contacts.rb

And here are the major files generated:

app\controllers\contacts_controller.rb

class ContactsController < ApplicationController
  # GET /contacts
  # GET /contacts.xml
  def index
    @contacts = Contact.all

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

  # GET /contacts/1
  # GET /contacts/1.xml
  def show
    @contact = Contact.find(params[:id])

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

  # GET /contacts/new
  # GET /contacts/new.xml
  def new
    @contact = Contact.new

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

  # GET /contacts/1/edit
  def edit
    @contact = Contact.find(params[:id])
  end

  # POST /contacts
  # POST /contacts.xml
  def create
    @contact = Contact.new(params[:contact])

    respond_to do |format|
      if @contact.save
        flash[:notice] = 'Contact was successfully created.'
        format.html { redirect_to(@contact) }
        format.xml  { render :xml => @contact, :status => :created, :location => @contact }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @contact.errors, :status => :unprocessable_entity }
      end
    end
  end

  # PUT /contacts/1
  # PUT /contacts/1.xml
  def update
    @contact = Contact.find(params[:id])

    respond_to do |format|
      if @contact.update_attributes(params[:contact])
        flash[:notice] = 'Contact was successfully updated.'
        format.html { redirect_to(@contact) }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @contact.errors, :status => :unprocessable_entity }
      end
    end
  end

  # DELETE /contacts/1
  # DELETE /contacts/1.xml
  def destroy
    @contact = Contact.find(params[:id])
    @contact.destroy

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

app\views\contacts\index.html.erb

<h1>Listing contacts</h1>

<table>
  <tr>
  </tr>

<% @contacts.each do |contact| %>
  <tr>
    <td><%= link_to 'Show', contact %></td>
    <td><%= link_to 'Edit', edit_contact_path(contact) %></td>
    <td><%= link_to 'Destroy', contact, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New contact', new_contact_path %>

app\views\contacts\show.html.erb

<%= link_to 'Edit', edit_contact_path(@contact) %> |
<%= link_to 'Back', contacts_path %>

app\views\contacts\new.html.erb

<h1>New contact</h1>

<% form_for(@contact) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.submit 'Create' %>
  </p>
<% end %>

<%= link_to 'Back', contacts_path %>

app\views\contacts\edit.html.erb

<h1>Editing contact</h1>

<% form_for(@contact) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.submit 'Update' %>
  </p>
<% end %>

<%= link_to 'Show', @contact %> |
<%= link_to 'Back', contacts_path %>

Any (further) advice would be greatly appreciated!

A: 

Have you written a migration for the contacts model?

Rails reads fields directly from the database and dynamically generates the attributes based on what's present on the table. If you just run the scaffold generator, the migration will still be empty. Once the migration is written, you can run the migrations with rake db:migrate and then I believe the fields should show up in the scaffold.

It has, however, been a very long time since I touched scaffolds. They are, IMHO, only useful when you're first learning Rails, and their use should be abandoned at the earliest possible moment.

Bob Aman
+1  A: 

I believe Rails will only add fields for the views it creates in a scaffold if you specify them at the time the scaffold is created, i.e. "script/generate scaffold post title:string body:text published:boolean" (this will also create a database migration for the table in question, by the way). I don't think it does any inspection of the database during scaffolding, but I haven't dug into the scaffold generation code to be absolutely certain.

Greg Campbell