Creating a Basic Question Interface

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.

Leave a Reply

Your email address will not be published. Required fields are marked *