Saturday, March 20, 2010

doing a survey with ruby on rails (part 1)

This is a basic tutorial that might help you understand the basics of ruby-on-rails. The idea of the project comes from this stackoverflow.com question. My current rails working environment is 2.3.5.

As with every rails project, in the beginning rails gives you a basic project setup using the model-view-controller pattern.

Let's try:


./ > rails -d mysql survey


The -d option is important to specify the usage of a database right at the start. In general, I use mysql for this, and the provided database.yml may need a bit tweeking the first time, but will soon be functional for many rails projects.

To create and test the database, you want to check with:


./survey/ > rake db:create


if there is no error message.

Coming back to our MVC pattern, at the moment, the directories /survey/app/models, /survey/app/views and /survey/app/controllers are still empty.

Basic resources (what the combination of a view-model-controller often is), can easily be done with scaffolding.

So, let's make a resource for our "question" and one resource for a "choice".


script/generate scaffold question whatabout:string
script/generate scaffold choice desc:string


With


rake db:migrate


we create our new tables in the database.

Now, we need to associate a choice wih a question. We want to select the choices that a user can enter in the survey. So, we use a m:n relationship between choices and questions. (The steps behind this are explained more in detail here, here and here).

So, to implement the associations, we need to modify our table and the models accordingly.

First, we create a helper table with:

script/generate model questions_choice


In the new migration, we add references to our "choices" and "questions" tables like this:

class CreateQuestionsChoices < ActiveRecord::Migration
def self.up
create_table :choices_questions, :id => false do |t|
t.references :choice, :question
t.timestamps
end
end

def self.down
drop_table :choices_questions
end
end


Let's check that we have no problems so far with a:

rake db:migrate


Next, we edit our models:

We say:


# question.rb
class Question < ActiveRecord::Base
has_and_belongs_to_many :choices
end

# choice.rb
class Choice < ActiveRecord::Base
has_and_belongs_to_many :questions
end

# questions_choice.rb
class QuestionsChoice < ActiveRecord::Base
belongs_to :question
belongs_to :choice
end


As a result, we should be able to access the table "Questions" from the table "Choices", and vice versa. "belongs_to", "has_and_belongs_to_many" are method calls of ActiveRecord. In a sense, Ruby let defines us our own domain-specific language easily, and that is basically what Rails is.

To show that the models are working, it is helpful to check with the Ruby interpreter first. For this, we start:


./survey/ > script/console -s


The -s option says we want to use a sandbox, i.e. our modifications in the database are rollbacked after we exit from the console.

So, first

q = Question.new
q.whatabout="Our Rails tutorial"
q.save


gives us a first question where we can add choices to. We do this with


c = Choice.new(:desc => "it's ok")
q.choices << c


Ruby knows from our models how to set the foreign key question_id and choice_id in the join table by using the operator "<<". We can repeat editing and adding new choices for our first question database.

Within the console, we can list all our choices for a question with:


q.choices.each {|choice| puts "#{choice.id} #{choice.desc}"}



In the next part of the tutorial, we will have a look on how we can provide a user interface for our models on questions and choices

No comments: