Finishing a Basic Student Interface and Building a Basic Output Interface
by tdaxp ~ June 24th, 2007
Getting up at 2:30 AM (completely accidentally) has its advantages. I listened to an episode of mysterious universe, got the below programming work done, read the paper, drank a pot of coffee, chatted with friends, ate two meals, and lumbered again… all before noon.

A laptop at work
Notes-on-Rails-wise, two main accomplishments today: finished up the student interface and also created a basic output interface. Neither of these are pretty, and there’s no CSS to be seen yet. However, this is solid progress, not only in the project, but as importantly in my understanding of the framework.

Behold!
The day’s work was built around a simple motto: “A good programmer is lazy, and rubyonrails makes it easy to be a good programmer.”
Instead of figuring out by reading documentation how to update students and notes into database, instead create static scaffolds for the students and notes model. Then we will integrate them into our student interface
First, edit the condition model with the line
has_many :students
Then generate two more static scaffolds, as we did on the second day
ruby script/generate scaffold student manage_students
ruby script/generate scaffold note manage_notes
That done, we will finish up the static scaffolds before we use code from here
Set the _form.rhtml partial in manage_student’s form partial to
<%= error_messages_for ‘student’ %>
<!–[form:notes_field]–>
<p><label for="student_name">Name</label><br/>
<%= text_field ‘student’, ‘name’ %></p>
<p><label for="student_condition_id">Condition ID</label><br/>
<%=
@conditions = Condition.find(:all,
rder => "name" ).map {|u| [u.name, u.id] }
select :student, :condition_id, @conditions
%></p>
<!–[eoform:notes_field]–>
This works, but the controller doesn’t show anymore… let’s fix that
Change show in manage_student’s controller to
def show
@student = Student.find(params[:id])
@student_name = @student['name']
condition = Condition.find(@student['condition_id'])
@student_condition = condition.name
end
and the _show.rhtml partial to
<p>
<br /><b>Name</b>: <%= @student_name %>
<br /><b>Condition</b>: <%= @student_condition %>
</p>
<%= link_to ‘Edit’, :action => ‘edit’, :id => @notes_record %> |
<%= link_to ‘Back’, :action => ‘list’ %>
For a reason I’m not clear about (how’s that for confidence!) the show function does not work, as it onyl shows the id but not other fields. But I have an idea.
We generated the scaffold before we generated the controller. Does this matter?
Who knows. But let’s go through the steps properly. First, let’s destroy our static scaffolds
Warning: This is a mistake, but unlike most mistakes, will wrongly destroy the student.rb and note.rb. I use jEdit to recover lost files!
ruby script/generate scaffold student manage_students
ruby script/generate scaffold note manage_notes
Now generate the controllers
ruby script/generate controller manage_students
ruby script/generate controller manage_notes
Now put in the line scaffold :student or scaffold :note, respectively, in these files.
Eep! The destroy accidentally destroyed student.rb and note.rb. No problem. I’ll open jEdit which I use for text editing and which autosaves everything, and recover it. Now it works fine, and we can even see the data we inputed.
That done, regenerate the stattic scaffold the same way we did before
ruby script/generate scaffold student manage_students
ruby script/generate scaffold note manage_notes
Woot! That works fine. So let’s go back now and rejigger manage_student’s _form like it was before.
And it works!
Now for the experiment… trying to put this code in the students controller (the one being worked on for the past few days).
First take that form we created and move it over to the _select_condition.rhtml partial in app/views/students
<%= form_remote_tag(:update => "notetaking_display",
:url => { :action => :select_notes_from_condition },
:position => "top" ) %>
Your Condition:
<%= select :student, :condition_id, @conditions %>
<p><label for="student_name">Your Name</label><br/>
<%= text_field ‘student’, ‘name’ %></p>
<%= submit_tag "Select Condition" %>
<%= end_form_tag %>
<div id="notetaking_display"></div>
And wahoo! While looking at the previously generated code (specifically app/controllers/manage_students_controller.rb#create) I see the .save() function that saves an object to the database. So change student_controller.rb#select_notes_from_condition to
def select_notes_from_condition
@student = Student.new(params[:student])
@student.save
@notes_fields = NotesField.find_by_condition(@student.condition_id)
@notes_records = NotesRecord.find_by_condition(@student.condition_id)
render :partial => “notes_view”
end
and the student is added to the database just fine.
Note to self: the next goal is to make sure that student is in the session and also save the notes. Using the universal .save method, we’ll do essentially the same thing here, too:
In app/controllers/students_controller.rb:
def select_notes_from_condition
@student = Student.new(params[:student])
@student.save
session[:student] = @student;
@notes_fields = NotesField.find_by_condition(@student.condition_id)
@notes_records = NotesRecord.find_by_condition(@student.condition_id)
render :partial => “notes_view”
end
def save_student_notes
@student = session[:student]
@notes_fields = NotesField.find_by_condition(@student.condition_id)
@notes_records = NotesRecord.find_by_condition(@student.condition_id)
for note_field in @notes_fields
for note_record in @notes_records
@note = Note.new(
:notetext => params[note_field.name][note_record.name],
:student_id => @student.id
)
@note.save
end
end
render_text “Notes Updated”
end
And let’s clean up the _notes_view partial a little too:
<h1>Note-Taking Matrix</h1>
<%= form_remote_tag(:update => "notetaking_display",
:url => { :action => :save_student_notes },
:position => "top" ) %>
<table border="1">
<tr>
<td> </td>
<% for notes_record in @notes_records %>
<td><%= h(notes_record.name) %></td>
<% end %>
</tr>
<% for notes_field in @notes_fields %>
<tr>
<td><%= notes_field.name %></td>
<% for notes_record in @notes_records %>
<td><%= text_area h(notes_field.name), h(notes_record.name), :cols => 20, :rows => 8 %></td>
<% end %>
</tr>
<% end %>
</table>
<%= submit_tag "Save Notes" %>
<%= end_form_tag %>
<div id="notetaking_display" name="notetaking_display"></div>
The last big thing to do is to create the code that will display the student records we already have. However, I accidentally deleted student.rb and note.rb. Thus I need to re-create them as they should be. student.rb should now be:
class Student < ActiveRecord::Base
belongs_to :condition
has_many :notes
def self.find_students(selected_condition_id)
@students = Student.find(:all, :conditions => ['condition_id = ?' , selected_condition_id ] )
end
end
and note.rb is:
class Note < ActiveRecord::Base
belongs_to :student
def self.find_notes(selected_student_id)
@students = Note.find(:all, :conditions => ['student_id = ?' , selected_student_id ] )
end
end
To do this, create a new controller called output
ruby script/generate controller output
First, some prepwork
in condition.rb, add the function
def self.find_conditions_array(selected_experiment_id)
@conditions = Condition.find(:all, :conditions => ['experiment_id = ?' , selected_experiment_id ] )
end
In the new app/controllers/output_controller.rb file, create a new function called index
def index
@experiments = Experiment.find(:all)
for experiment in @experiments
@conditions = Condition.find_conditions(experiment.id)
for condition in @conditions
@students = Student.find_students(condition)
for student in @students
@notes = Note.find_notes(condition)
end
end
end
end
Now for the app/view/index.rhtml
<html><head><title>Output for Notes on Rails</title></head>
<body>
<h1>Output Listing</h2>
<% for experiment in @experiments %>
<h2>Experiment <i><%= experiment.name %></i></h2>
<% for condition in Condition.find_conditions_array(experiment.id) %>
<h3>Condition <i><%= condition.name %></i></h3>
<% for student in Student.find_students(condition.id) %>
<h4>Student <i><%= student.name %></i></h3>
<% for note in Note.find_notes(student.id) %>
<p><%= note.notetext %></p>
<% end %>
<% end %>
<% end %>
<% end%>
</body>
</html>
Roh roh! A quick test reveals that the notes table does not save record or field information, making it impossible to reconstruct what went where. This can be solved by retrofitting some code and doing a new migration, but it’s still frustrating.
Go back in the notes to when the basic administration interface was finished to alter the notes table
ruby script/generate migration alter_notes
This generates 014_alter_notes.rb, which should read (after editing)
class AlterNotes < ActiveRecord::Migration
def self.up
add_column :notes, :notes_field_id, :integer
add_column :notes, :notes_record_id, :integer
end
def self.down
remove_column :notes, :notes_field_id
remove_column :notes, notes_record_id
end
end
Then rake db:migrate
In notes_field.rb and notes_record.rb, add the function (from Agile Web Development with Rails, 28)
def self.name_from_id(selected_id)
@entries = find(selected_id);
@entries.name
end
Then, index.rhtml in output’s view:
<html><head><title>Output for Notes on Rails</title></head>
<body>
<h1>Output Listing</h2>
<% for experiment in @experiments %>
<h2>Experiment <i><%= experiment.name %></i></h2>
<% for condition in Condition.find_conditions_array(experiment.id) %>
<h3>Condition <i><%= condition.name %></i></h3>
<% for student in Student.find_students(condition.id) %>
<h4>Student <i><%= student.name %></i></h3>
<% for note in Note.find_notes(student.id) %>
<p><i>On the <%= NotesField.name_from_id(note.notes_field_id) %> of <%= NotesRecord.name_from_id(note.notes_record_id) %></i>: <%= note.notetext %></p>
<% end %>
<% end %>
<% end %>
<% end%>
</body>
</html>
And your done!
Tomorrow: making it pretty.