I approached the task of undertaking market research for my new music start-up company in the typical programmer way: I made a website. I wonder how many of life’s problems I would approach in this way; but having a website seems like a good start.
I wanted to canvass the opinion of friends and colleagues on the habits of their on-line music consumption, particularly pertaining to music subscriptions, which is a central idea within my business plan. I knew that I wasn’t an expert in marketing, so I wanted to design a form that would allow users to customise it as they went; so that they could add choices for others to select as they went along. I accomplished this using a standard ruby on rails website, a form, and some AJAX.
Defining my data
First of all, I wrote down a basic entity-relationship diagram on the back of an envelope of what I wanted my models to look like. I knew I was going to ask a load of questions, so I made a box to represent them. I also knew that each question would have a number of choices to select from, such as ‘Agree’ and ‘Disagree’, which is a one-to-many relationship. Next, I hoped that someone would respond to the questionnaire, by associating a subset of choices with the questions I had asked, so I made a box for ‘responses’. There were multiple choices in the questionnaire and hopefully multiple choices so I needed a many-to-many relationship.

I even threw in a user model to store a respondant’s age and location; although it was probably overkill.
Iteration #1 – Make some models
I could then ask rails to do lots of work for me using the generate scripts.
ruby script/generate model Question text:text
ruby script/generate model Choice text:string, question_id:integer
ruby script/generate model Response question_id:integer, choice_id:integer, user_id:integer
ruby script/generate model User age:string, location:string
Normally I’d start with a scaffold, but in this case I didn’t want all of the CRUD features for questions and choices available on-line. Instead, I borrowed a trick I’d learnt from hacking around on the hyperactive CMS project to ’seed’ the database by loading in data from YML files, rather like the way fixtures work in the unit tests.
Before I started-up webbrick, I wrote down some unit tests to ensure that the relationships were working the way I expected them to, and to ensure that the basic validation on the models were working correctly.
class QuestionTest < ActiveSupport::TestCase
test "required fields" do
question = Question.new
assert_equal false, question.save
question.text = hi
assert question.save
end
test "question has choices" do
assert_equal false, questions(:one).choices.empty?
assert_equal choices(:emusic), questions(:one).choices.first
end
end
The first test makes sure I can’t save a question without the question’s text being populated. Rather than testing the rails validation framework, this gives me the peace of mind that I remembered to write the validation, and the test will tell me if I accidentally remove the validation in a future re-factor.
The second test ensures that I have my model relationships configured correctly, so that I can access a question’s choices via an attribute.
I could then go away and implement my model:
class Question < ActiveRecord::Base
validates_presence_of :text
has_many :choices, :o rder => choice_index, text
end
I approached the remaining models in the same way. Before I could see anything in a browser, I needed to populate the database using the YML files I mentioned before. So I wrote the following rake task to populate the database (lives in lib/tasks).
namespace :db do
desc "Load seed fixtures (from db/seed) into the current environment is database."
task :seed => :environment do
require active_record/fixtures
Dir.glob(RAILS_ROOT + /db/seed/*.yml).each do |file|
Fixtures.create_fixtures( wouldb/seed, File.basename(file, .*))
end
end
end
Which allowed me to structure the data of my questionnaire in the following way:
(db/seed/questions.yml)
three:
id: 3
text: What music websites do you currently use?
(db/seed/choices.yml)
emusic:
question_id: 3
text: E-music
napster:
question_id: 3
text: Napster
spotify:
question_id: 3
text: Spotify
I could then run rake db:seed and my database would be populated with the questionnaire data. I could now fire-up a browser and begin programming the view.
Iteration #2: First form
The next job was to create a form that would allow my respondents to view the questions in my database, along with the choices, and make some responses. I had a bit of plumbing to do here, without the scaffold in place, so I generated a controller, added some views and updated by routes.rb.
Once I had a view, I could create a form. I decided to treat the form as an ordered-list, so I created a partial that would supply the HTML for each question and enclosed it in a OL and LI tag:
<% form_tag /respond do -%>
<ol>
<% @questions.each do |q| %>
<%=render :partial => questionnaires/question, :locals => { :q => q } %>
<% end %>
</ol>
<div id="submit">
<p>That is all for now! Press this button to complete the survey.</p>
<%=submit_tag Submit Questionnaire\%>
</div>
<% end -%>
This approach immediately posed the problem of how to read in the form data, when the view doesn’t know what questions and choices are being visualised in advance. The solution rested in the correct naming convention that allowed rails to convert the form data into a manageable collection. I wanted a collection of responses, that would each contain a collection of those selected choices a user had made, and this is done by naming each input ‘responses[]X[]‘, where X is the id of the question being asked. This results in a nice collection called ‘responses’, which I can access in the controller through params[:responses], each containing a collection of choices organised by their question_id.
So I could code my question partial (q is the current question):
<% question_name = aresponses[] + q.id.to_s + [] %>
<li class="question" >
<%=label_tag ext + q.id.to_s, q.text%>
<%=text_field_tag question_name, nil, {:id=> ext + q.id.to_s} %>
</li>
However, I realised that I couldn’t determine what type of question is was yet; should I display a text-box, a check-box or a radio-box? Back to the drawing-board.
Iteration #3: Multiple-choices
I needed a way of determining what type of question I was asking: would I allow multiple responses to the same question (appropriate when asking ‘which of the following…), or only one (agree, disagree). I accomplished this by adding a allow_multiple flag to my question model, which I could then read in the view to determine whether or not to display a checkbox (for multiple responses) or a radio-box (for one-choice). When there were no choices for a question, I would assume a text-box was reuqired, and that is how the age/location fields work.
Once I had updated my fixtures, model and database, I could return to the view:
<% if q.choices.empty? %>
<%=label_tag ext + q.id.to_s, q.text%>
<%=text_field_tag question_name, nil, {:id=> ext + q.id.to_s} %>
<% else %>
<p><%=q.text%></p>
<% q.choices.each do |c| %>
<div class="choice">
<%=label_tag choice + c.id.to_s, c.text%>
<% if q.multiple_choice %>
<%=check_box_tag question_name, c.id, nil, :id=> choice + c.id.to_s%>
<% else %>
<%=radio_button_tag question_name, c.id, nil, :id=> choice + c.id.to_s%>
<% end %>
</div>
<% end %>
I could now view my form and see the correct input fields being displayed, ready for my controller to save the responses.
Iteration #4: Adding new choices
The main reason for developing a new questionnaire in rails was to allow my respondents to add choices which I hadn’t anticipated, in turn allowing others to choose the same response. This is proving useful for the ‘which music services do you use’ question, throwing-up websites I might not have otherwise heard of and I can now benchmark and investigate.
To add a new choice, I needed a way of distinguishing which questions I would allow new choices for and which I would not, so I added another field to question called ‘allow_new’. I updated my model, fixture, database (and unit test of course!) and was ready to up-date my view.
<% if q.allow_new %>
<div class="choice">
<label>Other (please specify)</label>
<div id="new<%=q.id%>">
<%=text_field_tag ewchoice, nil, {:id => ewchoice + q.id.to_s} %>
<%=hidden_field_tag question_id, q.id, {:id => question_id + q.id.to_s} %>
<%=submit_to_remote add_btn, Add, :update => (q + q.id.to_s),
:url => /addchoice, :submit => q + q.id.to_s%>
</div>
</div>
<% end %>
I added a textfield labelled ‘Other – please specify’ and a button with an AJAX action, so that respondants could add their new choice as they went along, without submitting the entire form. In order to achieve that, I used the submit_to_remote button, which in effect allowed me to nest multiple forms within a single form. Although intuitively, I wanted to create additonal form tags to surround the new text field, this approach didn’t seem to work cross-browser and only the one form is needed. Instead, the :submit option is used to tell javascript to serialise all of the form elements underneath the element with the given ID. The beauty is that this element doesn’t have to be a form element- so here I use the DIV surrounding the question, serialising only the text field for the new choice, along with the existing choices (which I later used to pre-populate the choices that had already been selected). I also pass along the question_id in a hidden field, so that the controller knows which question the new choice belongs to.
IE GOTCHAS: Developers beware! This approach of including multiple AJAX submit buttons in IE is very fickle; unless you play by its rules it wont work and you’ll probably waste time trying to figure it out. Don’t fall into these traps:
1. You cannot nest multiple AJAX forms inside a larger form. It might work in Firefox, but IE won’t know which button you’ve pressed and won’t submit or update your chosen element properly. I found that I couldn’t even submit the main form using this approach!
2. You cannot nominate any element for the :submit option. I tried using the <li> element at first, but in the end I surrounded the entire <li> with a <div>, and updated that- which works fine cross-browser.
Iteration #5: Finishing up
I had a working questionniare, but I wanted to ensure that at least the age and location fields were completed, and that any previously-completed form elements were not lost in the event that validation failed.
The first task here is to ensure that the user model validates for this information:
class User < ActiveRecord::Base
validates_presence_of :location
validates_numericality_of :age, :allow_nil => false
has_many :responses
end
Now if I try to call save! on the User class, I’ll get an error in my controller. Using a begin/rescue block, I could now take appropriate action:
rescue
@errors = true
@questions = Question.find(:all, :o rder => question_order)
render :action => index
end
end
I didn’t want my validation to be too sophisticated. Here I used a flag stored in @errors that I check for in my view to display an error message.
To ensure that the form pre-populates with the previous responses, I had to pass the relevant data back out of the params object as the value parameter for the form tag helpers. In the case of the checkboxes, these values would be within a collection that stores the primary key of the choices that had been made, so a little logic was needed to detmermine whether or not to check the box controls:
checked = prevvalue.include?(c.id.to_s) or c.text == params[:newchoice]
The second test here checks the new choice created by the AJAX form, determined by whether or not the text in the submitted box matches the newly-created test.
So, an afternoon invested in a new questionnaire and hopefully some intriguing insights into our music consumption habits. If you have any observations about my approach here, or have spotted something that I could have done better or more efficiently, please leave a comment as I’d be glad to know about it.
Related Stuff
-
MooV: Using cutting edge Video phones and Software Video Phones - coupling all that with VoIP and empowering the disabled.
-
Moo Telecom: VoIP communications made easy - Ring anyway with the fun and ease of using a normal phone
-
TagR:Mobile Social Network with Real Time Locations Based services, and Ambience Intelligence, VoiP, IM, Skype, Googletalk, Mapping, Flickr, Events, Calendaring, Scheduling, SecondLife Support
-
ClearSMS : ClearSMS is a Web-based application that lets you send bulk SMS messages to your customers, contacts, or just about anyone.
-
Jajah:jah is a VoIP (Voice over IP) provider, founded by Austrians Roman Scharf and Daniel Mattes in 2005[1]. The Jajah headquarters are located in Mountain View, CA, USA, and Luxembourg. Jajah maintains a development centre in Israel.
-
Skype: It’s free to download and free to call other people on Skype. Skype the number one voice over ip software
- PrivatePhone: a free local phone number with voicemail and messages you can check online or from any phone.

Original Source: