Ruby on Rails — Demonstration

This demonstration assumes that you have Ruby, Ruby on Rails and MySQL installed and working correctly, with the MySQL daemon currently running. For information on downloading and installing both Ruby and Rails, please see the download instructions from the Ruby on Rails website. For information on downloading and installing MySQL, please see the MySQL Download page.

Alternatively, if you are using Windows or Mac OS X, you might want to check Instant Rails or Locomotive, respectively. These packages should contain everything you need (I personally haven't tried either, though, so your mileage may vary).

Also note that these instructions are really geared towards a Linux audience who are comfortable with the command line. Changes may be required to get things working properly under Windows or Mac OS X, but hopefully, these changes should be minor.

This demonstration was inspired by the Screen Cast — "Creating a weblog in 15 minutes" .mov file.

  1. Configure the MySQL database. The mysql command below will disallow remote connections to the MySQL database, disallow anonymous local users from accessing the database, assign a root password and add a user called emrdev that can access a database called emr_development.

    Note: The command below assumes that your MySQL database is a fresh installation that is not being used for anything else. If this is not true, then running the command below may not be a good idea. (It may not even work if the root user of your MySQL database has already been configured with a password.) Also, you should use different passwords, ideally. Graphical tools can be used to do these steps, if desired.

    $ mysql -u root -e \
    "delete from mysql.user where host <> 'localhost'; \
     delete from mysql.user where user <> 'root'; \
     update mysql.user set password=password('r0Rt357') where user = 'root'; \
     grant all on emr_development.* to 'emrdev'@'localhost' identified by 'd3v1'; \
     flush privileges;"
    
    If your database is already being used and already has a root user, then the following command may be better to use:
    $ mysql -u root -p -e \
    "grant all on emr_development.* to 'emrdev'@'localhost' identified by 'd3v1'; \
     flush privileges;"
    
  2. Create the directory structure for your web application. In a preferably uncluttered directory, run the command:
    $ rails emr
    
    This will generate output indicating what files and directories are being created. Everything will be located in the newly created directory emr.
  3. Change into the emr directory. All modifications will take place in this directory created by the rails command above. To become familiar with the directory structure, examine it with a recursive directory listing or with a file browser. The most important directories for now will be app/, script/ and config/.
    $ cd emr
    $ ls -RFC
    
  4. Let Rails know the development database username and password. This information should be addd to the config/database.yml file. In particular, modify the username and password field of the development: stanza in the configuration file.
    development:
      adapter: mysql
      database: emr_development
      username: emrdev
      password: d3v1
      socket: /tmp/mysql.sock
    
  5. Create a database table schema. Note that you can create schemas directly in Rails by using migrations, but we'll stick with the mysql program for now. Note that the table name is plural.
    $ mysql -u emrdev -pd3v1 -e \
    "DROP DATABASE if exists emr_development;
    
    CREATE DATABASE emr_development;
    
    USE emr_development;
    
    CREATE TABLE patients (
      id             int unsigned   NOT NULL auto_increment,
      given_names    varchar(64)    NOT NULL,
      last_name      varchar(64)    NOT NULL,
      mcp_number     varchar(12)    NOT NULL,
    
      PRIMARY KEY(id)
    );"
    
  6. Using the Rails generate script, generate the code for the Rails model corresponding to the patients database table. You should be in the emr directory when running this command. Note that the class name is singular.
    $ ./script/generate model Patient
    
    Again, more output will be produced indicating what files and/or directories were created. The model is generated in the file app/models/patient.rb. (It's only two lines long!)
    $ cat app/models/patient.rb
    
  7. Use the console utility to get practice with Rails ORM functionality.
    $ ./script/console
    Loading development environment.
    >> pat = Patient.new
    >> pat.given_names = "Homer J."
    >> pat.last_name = "Simpson"
    >> pat.mcp_number = "123456789012"
    >> pat.save
    >> Patient.create(:given_names => "Marge",
    ?>                :last_name   => "Simpson",
    ?>                :mcp_number  => "210987654321")
    >> require 'pp'
    >> pp Patient.find(1)
    >> pp Patient.find_by_last_name("Simpson")
    >> pp Patient.find_all_by_last_name("Simpson")
    >> pat = Patient.find_by_given_names("Homer J.")
    >> pat.given_names = "Homer Jay"
    >> pat.save
    >> pp Patient.find(1)
    
  8. Confirm that the data has indeed by stored in the database.
    $ mysql -u emrdev -pd3v1 emr_development -e 'select * from patients'
    
  9. Generate Scaffolding. In order to be able to create, read, update and delete patients from a web page, we must create a controller and corresponding views that can interact with our Patient model. We can use Rails' scaffolding to generate a primitive, but functional, web application. Note that this may try to overwrite some of the files that already exist in the emr directory (you will get a warning).
    $ ./script/generate scaffold Patient Clinic
    
    The important files generated include a controller: app/controllers/clinic_controller.rb and a collection of associated views in app/views/clinic/ directory. Observe that for many of the methods in the controller, there exist a corresponding view. Also note that the _form.rhtml partial template file is shared by the new.rhtml and edit.rhtml views, because the form is almost identical in both cases.
  10. Start up a web server. Rails comes with a simple ruby-based webserver called WEBrick which is sufficient for demonstration purposes. This will generate output each time requests are made of the server. You should probably run it in a different terminal window so that you can monitor its output.
    $ ./script/server
    
  11. In another terminal window, it may be a good idea to view the log/development.log file so that you can monitor interactions between the model, views and controller. You can also see the SQL statements that are being created and executed on the database via the ORM layer.
    $ clear; tail -n 0 -f log/development.log
    
  12. Using a web browser, visit the root Rails page http://localhost:3000/. This provides you with information about the Rails setup.
  13. Visit the controller generated by the scaffolding at the URL http://localhost:3000/clinic/. Note that by default, the index action is called which simply renders to the list view.
  14. To test the scaffolding, add some patients to the database and then edit them.
  15. Add some validations to the Patient model: app/models/patient.rb.
    class Patient < ActiveRecord::Base
      validates_presence_of    :given_names, :last_name
      validates_uniqueness_of  :mcp_number
      validates_format_of      :mcp_number,
                               :with => /^\d{12}$/,
                               :message => "must be 12 digits"
    end
    
  16. Try adding patients without names, or with invalid or non-unique MCP numbers. The validation will tell you of errors.
  17. Add new fields to the patient model. (Note that we remove the old database before creating the new schema.)
    $ mysql -u emrdev -pd3v1 -e \
    "DROP DATABASE if exists emr_development;
    
    CREATE DATABASE emr_development;
    
    USE emr_development;
    
    CREATE TABLE patients (
      id           int unsigned NOT NULL auto_increment,
      given_names  varchar(64)  NOT NULL,
      last_name    varchar(64)  NOT NULL,
      mcp_number   varchar(12)  NOT NULL,
      address1     varchar(64)  NOT NULL,
      address2     varchar(64)  NOT NULL,
      dob          date         NOT NULL,
    
      PRIMARY KEY(id)
    );"
    
  18. Regenerate the scaffolding so that the forms will be updated.
    $ ./script/generate scaffold Patient Clinic
    
    Allow it to overwrite files.
  19. Observe that new table header columns are displayed in the patient listing; the list.rhtml view did not change because its code is generic enough to handle changes to the columns. The clinic_controller.rb file is also sufficiently generic enough to allow for changes in the table attributes without having to be changed itself.
  20. Observe that the new fields are now present in the new/edit patient forms. The generate script modified the common partial app/views/clinic/_form.rhtml template. The date field isn't very flexible, so we'll change it to a text_field in app/views/clinic/_form.rhtml partial. This will allow much more flexible date input.
    <%= error_messages_for 'patient' %>
    
    <!--[form:patient]-->
    <p><label for="patient_given_names">Given names</label><br/>
    <%= text_field 'patient', 'given_names'  %></p>
    
    <p><label for="patient_last_name">Last name</label><br/>
    <%= text_field 'patient', 'last_name'  %></p>
    
    <p><label for="patient_mcp_number">Mcp number</label><br/>
    <%= text_field 'patient', 'mcp_number'  %></p>
    
    <p><label for="patient_address1">Address1</label><br/>
    <%= text_field 'patient', 'address1'  %></p>
    
    <p><label for="patient_address2">Address2</label><br/>
    <%= text_field 'patient', 'address2'  %></p>
    
    <p><label for="patient_dob">Dob</label><br/>
    <%= text_field 'patient', 'dob'  %></p>
    <!--[eoform:patient]-->
    
    
  21. We'll also add appropriate validations to the app/models/patient.rb file.
    class Patient < ActiveRecord::Base
      validates_presence_of   :given_names, :last_name, :address1, :address2
      validates_uniqueness_of :mcp_number
      validates_format_of     :mcp_number,
                              :with => /^\d{12}$/,
                              :message => "must be 12 digits"
      validates_format_of     :dob,
                              :with => /^\d\d\d\d-\d\d-\d\d$/,
                              :message => "must be a valid date"
    end
    
  22. Add the chart model and generate a model for it.
    $ mysql -u emrdev -pd3v1 emr_development -e \
    "CREATE TABLE charts (
      id          int unsigned NOT NULL auto_increment,
      patient_id  int unsigned NOT NULL,
      comment     varchar(255) NOT NULL,
      weight      int(4)       NOT NULL,
      height      int(4)       NOT NULL,
      date        date         NOT NULL,
    
      PRIMARY KEY(id)
    );"
    $ ./script/generate model Chart
    
  23. Update both the Patient and Chart models to reflect the relationship that exists between them (a patient has many charts and a chart belongs to a patient).

    app/models/patient.rb:

    class Patient < ActiveRecord::Base
      validates_presence_of   :given_names, :last_name, :address1, :address2
      validates_uniqueness_of :mcp_number
      validates_format_of     :mcp_number,
                              :with => /^\d{12}$/,
                              :message => "must be 12 digits"
      validates_format_of     :dob,
                              :with => /^\d\d\d\d-\d\d-\d\d$/,
                              :message => "must be a valid date"
      has_many :charts
    end
    

    app/models/chart.rb:

    class Chart < ActiveRecord::Base
      belongs_to :patient
    end
    
  24. Modify the app/views/clinic/show.rhtml view to display the chart information for a patient.

    app/views/clinic/show.rhtml:

    <% for column in Patient.content_columns %>
    <p>
      <b><%= column.human_name %>:</b> <%=h @patient.send(column.name) %>
    </p>
    <% end %>
    
    <%= link_to 'Edit', :action => 'edit', :id => @patient %> |
    <%= link_to 'Back', :action => 'list' %>
    
    <table>
    <tr>
    <% for column in Chart.content_columns %>
      <th><%= column.human_name %>
    <% end %>
    
    <% for chart in @patient.charts %>
      <tr>
      <% for column in Chart.content_columns %>
        <td><%=h chart.send(column.name) %>
      <% end %>
    <% end %>
    </table>
    
  25. Populate the database with the script pop-patcharts.rb, move it to the db directory and feed it as input to the ./script/console command:
    $ ./script/console < db/pop-patcharts.rb
    
    Each patient should now have multiple charts which can be viewed using the Show link next to each patient.
  26. Modify the show.rhtml view to include a form that will let new charts be created.

    app/views/clinic/show.rhtml:

    <% for column in Patient.content_columns %>
    <p>
      <b><%= column.human_name %>:</b> <%=h @patient.send(column.name) %>
    </p>
    <% end %>
    
    <%= link_to 'Edit', :action => 'edit', :id => @patient %> |
    <%= link_to 'Back', :action => 'list' %>
    
    <table>
    <tr>
    <% for column in Chart.content_columns %>
      <th><%= column.human_name %>
    <% end %>
    
    <%= start_form_tag :action => 'chartcreate', :id => @patient %>
      <tr>
      <td><%= text_field 'chart', 'comment' %>
      <td><%= text_field 'chart', 'weight', :size => 6  %>
      <td><%= text_field 'chart', 'height', :size => 6  %>
      <td><%= text_field 'chart', 'date', :size => 20  %>
    
      <td><%= submit_tag "Create" %>
    <%= end_form_tag %>
    
    <% for chart in @patient.charts %>
      <tr>
      <% for column in Chart.content_columns %>
        <td><%=h chart.send(column.name) %>
      <% end %>
    <% end %>
    </table>
    
  27. Modify the app/controllers/clinic_controller.rb to add the createchart action.
    class ClinicController < ApplicationController
      def index
        list
        render :action => 'list'
      end
    
      def list
        @patient_pages, @patients = paginate :patients, :per_page => 10
      end
    
      def show
        @patient = Patient.find(params[:id])
      end
    
      def new
        @patient = Patient.new
      end
    
      def create
        @patient = Patient.new(params[:patient])
        if @patient.save
          flash[:notice] = 'Patient was successfully created.'
          redirect_to :action => 'list'
        else
          render :action => 'new'
        end
      end
    
      def chartcreate
        Patient.find(params[:id]).charts.create(params[:chart])
        flash[:notice] = 'Chart successfully created.'
        redirect_to :action => 'show', :id => params[:id]
      end
    
      def edit
        @patient = Patient.find(params[:id])
      end
    
      def update
        @patient = Patient.find(params[:id])
        if @patient.update_attributes(params[:patient])
          flash[:notice] = 'Patient was successfully updated.'
          redirect_to :action => 'show', :id => @patient
        else
          render :action => 'edit'
        end
      end
    
      def destroy
        Patient.find(params[:id]).destroy
        redirect_to :action => 'list'
      end
    end
    
  28. Now charts can be added directly via the web page that shows all the charts for a patient.
  29. Shutdown the webserver and the tailing of the development.log. Go to the appropriate terminal windows and hit ^C.
  30. Shutdown the mysql server.
    $ mysqladmin -u root -pr0Rt357 shutdown
    

Donald Craig (donald@mun.ca)
Last modified: March 23, 2006 22:45:02 NST
Valid XHTML 1.0 Strict Valid CSS!