Having created a basic question-list interface (functionally the same as MediaLab’s que file), today the admin interface is expanded to have a basic question interface.
The standard static scaffold is actually inappropriate for how we’ll want questions to be generated (something like the experiment controller’s ajax will be require), but for now let’s just get it working.
One major change is we will create a Class Constant (enumeration) for our question types, with apologies to RubyFleebie:
Create a module named question_type.rb
class QuestionType
def self.add_item(key,value)
@hash ||= {}
@hash[key]=value
end
def self.const_missing(key)
@hash[key]
end
def self.each
@hash.each {|key,value| yield(key,value)}
end
def self.find_selection_list
@hash
end
QuestionType.add_item :STRING, 1
QuestionType.add_item :TEXT, 2
QuestionType.add_item :SELECTION, 3
QuestionType.add_item :CHECKBOX, 4
QuestionType.add_item :RADIO, 5
end
Change question.rb:
class Question < ActiveRecord::Base
validates_presence_of :name, :display, :type, :list_id
validates_uniqueness_of :name
end
make sure question_list.rb is to
class QuestionList < ActiveRecord::Base
validates_presence_of :name, :description
def self.find_name_by_id(selected_id)
found_object = find(selected_id)
@to_return = found_object.name
end
def self.find_selection_list
@conditions = QuestionList.find(:all ).map {|u| [u.name, u.id] }
end
end
Uh oh. I realized I may have used another special name in a migration like I did on day one. No problem. Generate a new migration:
019_alter_questions should be:
class AlterQuestions < ActiveRecord::Migration
def self.up
add_column :questions, :type_id, :integer
remove_column :questions, :type
end
def self.down
remove_column :questions, :type_id
add_column :questions, :type_id, :integer
end
end
Then rake db:migrate
app/views/manage_questions/_form.rhtml:
<%= error_messages_for ‘question’ %>
<!–[form:question]–>
<p><label for="question_name">Name</label><br/>
<%= text_field :question, :name %></p>
<p><label for="question_display">Display</label><br/>
<%= text_area :question, :display, :rows => 3 %></p>
<p><label for="question_type_id">Type</label><br/>
<% @question_types = QuestionType.find_selection_list %>
<%= select :question, :type_id, @question_types %>
<p><label for="question_list_id">Question List</label><br/>
<% @question_lists = QuestionList.find_selection_list %>
<%= select :question, :list_id, @question_lists %>
<!–[eoform:question]–>
Apparently, display is a special word too, so create yet another migration…
ruby script/generate migration alter_questions_replace_display_with_display_text
And make 020_alter_questions_replace_display_with_display-text.rb like so:
class AlterQuestionsReplaceDisplayWithDisplayText < ActiveRecord::Migration
def self.up
remove_column :questions, :display
add_column :questions, :display_text, :text
end
def self.down
remove_column :questions, :display_text
add_column :questions, :display, :text
end
end
Then rake db:migrate
Now to to question.rb, _form.rhtml, list.rhtml changing names as appropriate
Now, only one more thing to do: create default options where appropriate.
_form should now look like:
<%= error_messages_for ‘question’ %>
<!–[form:question]–>
<p><label for="question_name">Name</label><br/>
<%= text_field :question, :name %></p>
<p><label for="question_display">Display</label><br/>
<%= text_area :question, :display_text, :rows => 3 %></p>
<p><label for="question_type_id">Type</label><br/>
<% @question_types = QuestionType.find_selection_list %>
<%= select :question, :type_id, @question_types %>
<p><label for="question_list_id">Question List</label><br/>
<% @question_lists = QuestionList.find_selection_list %>
<%= select :question, :list_id, @question_lists %>
<p><label for="question_options">Default Options</label><br />
<% 1.upto(10) do |iterator| %>
<%= text_field :question_options, iterator %>
<% end %></p>
<!–[eoform:question]–>
Now we need to add default option awareness to the controller. Again, >yeseterday’s work will be a guide.
But first, another change (aren’t you glad we have migrations?)
ruby generate/migration alter_question_options
021_alter_question_options.rb:
class AlterQuestionOptions < ActiveRecord::Migration
def self.up
remove_column :question_options, :name
remove_column :question_options, :optiontext
add_column :question_options, :option_id, :integer
add_column :question_options, :display_text, :text
end
def self.down
remove_column :question_options, :option_id
remove_column :question_options, :display_text
add_column :question_options, :name, :string
add_column :question_options, :optiontext, :text
end
end
Then rake db:migrate
Now, update two functions in manage_questions_controller.rb
def create
#render_text params[:question][:display]
@question = Question.new(params[:question])
@result_of_save = @question.save
if @result_of_save
@question_options = params[:question_options]
for question_option in @question_options
@question_option_id = question_option[0].to_i
@question_option_text = question_option[1].to_s
if @question_option_text
QuestionOption.add_by_question_id_option_id(@question.id,@question_option_id,@question_option_text)
else
QuestionOption.destroy_by_question_id_option_id(@question.id,@question_option_id)
end
end
flash[:notice] = ‘Question was successfully created.’
redirect_to :action => ‘list’
else
render :action => ‘new’
end
end
def edit
@question = Question.find(params[:id])
end
def update
@question = Question.find(params[:id])
if @question.update_attributes(params[:question])
@question_options = params[:question_options]
for question_option in @question_options
@question_option_id = question_option[0].to_i
@question_option_text = question_option[1].to_s
if @question_option_text
QuestionOption.add_by_question_id_option_id(@question.id,@question_option_id,@question_option_text)
else
QuestionOption.destroy_by_question_id_option_id(@question.id,@question_option_id)
end
end
flash[:notice] = ‘Question was successfully updated.’
redirect_to :action => ’show’, :id => @question
else
render :action => ‘edit’
end
end
Change the question_option.rb module:
class QuestionOption < ActiveRecord::Base
validates_presence_of :question_id, :option_id, :display_text
def self.add_by_question_id_option_id(question_id,option_id,display_text)
self.destroy_by_question_id_option_id(question_id,option_id)
to_add = self.new(
:question_id => question_id,
:option_id => option_id,
:display_text => display_text
)
to_add.save
return to_add
end
def self.destroy_by_question_id_option_id(question_id,option_id)
to_destroy = find_by_question_id_option_id(question_id,option_id)
to_destroy.destroy if to_destroy
end
def self.find_by_question_id_option_id(question_id,option_id)
to_return = find(
:first,
:conditions => {
:question_id => question_id,
:option_id => option_id
}
)
to_return
end
end
The very last things that we need to do is to work on the manage_question_list_conditions_controller (which as I’ve added and deleted data spontaneously stopped working) and the question_options controller (so we can see the options we just added — sometime later we will integrate this functionality with the main controller)
The specific problem with the QuestionListConditionsController reads:
Showing app/views/manage_question_list_conditions/list.rhtml where line #13 raised:
Couldn’t find QuestionList with ID=6
With the extracted source:
<tr>
<td><%= question_list_condition.id %></td>
<td><%= Condition.find_name_by_id(question_list_condition.condition_id) %></td>
<td><%= QuestionList.find_name_by_id(question_list_condition.question_list_id) %></td>
</tr>
<% end %>
</table>
So what we need to do is to alter QuestionList’s module’s find_name_by_id to encorporate some basic exception handling (actually we should have exception handling all over the place)
So change QuestionList:find_name_by_id to
def self.find_name_by_id(selected_id)
begin
found_object = find(selected_id)
@to_return = found_object.name
rescue
@to_return = “Error: No Name found for id ” + selected_id.to_s + ” generated by question_list.rb:find_name_by_id”
end
@to_return
end
(not exactly pretty, but right now focus on the structure).
Update question.rb:
class Question < ActiveRecord::Base
validates_presence_of :name, :display_text, :type_id, :list_id
validates_uniqueness_of :name
def self.find_name_by_id
to_find = find(selected_argument_id)
@to_return = to_find.name
end
end
And finally app/views/manage_question_options/list.rhtml
<h1>Listing question_options</h1>
<table>
<tr>
<th>ID</th>
<th>Question ID</th>
<th>Question Text</th>
<th>Option</th>
<th>Display Text</th>
</tr>
<% for question_option in @question_options %>
<tr>
<td><%= question_option.id %></td>
<td><%= question_option.question_id %></td>
<td><%= Question.find_name_by_id(question_option.question_id) %></td>
<td><%= question_option.option_id %></td>
<td><%= question_option.display_text %></td>
<td><%= link_to ‘Show’, :action => ’show’, :id => question_option %></td>
<td><%= link_to ‘Edit’, :action => ‘edit’, :id => question_option %></td>
<td><%= link_to ‘Destroy’, { :action => ‘destroy’, :id => question_option }, :confirm => ‘Are you sure?’, :method => :post %></td>
</tr>
<% end %>
</table>
<%= link_to ‘Previous page’, { :page => @question_option_pages.current.previous } if @question_option_pages.current.previous %>
<%= link_to ‘Next page’, { :page => @question_option_pages.current.next } if @question_option_pages.current.next %>
<br />
<%= link_to ‘New question_option’, :action => ‘new’ %>
Another days work done!
Tomorrow: showing the defaults on the questions and question-lists controller.