2

I'm somewhat new to Ruby and Rails and I'm trying to figure out how to model the following relation using Ruby's dynamic language features....

The domain is essentially a questionnaire, so I have Users, Questions, and Answers.

A User has an id and a name. An Answer has a userid and a questionid and a value property which is the user's answer to that particular question.

Each question has a unique 'Code', so for example, a question might be, "What is your favourite color?", and code would be "fav_color".

If I have a User instance, I want to be able to do: user.fav_color to get/set this answer.

I figure I could try and user method_missing and then make a finder call like in this example: http://rpheath.com/posts/241-how-to-use-method-missing

Or can I dynamically add properties to a class at run-time like here:

Ruby - dynamically add property to class (at runtime)

But I need to read / write to the database....simply storing these dynamic properties in an instance variable is no good for me.

Any help would be much appreciated...or advice on a better way to approach this problem :)

Community
  • 1
  • 1
Darragh
  • 2,526
  • 1
  • 23
  • 31

2 Answers2

1
code = "fav_color"
User.class_eval do
  define_method code do
    read_attribute code
  end
  define_method "#{code}=" do |value|
    write_attribute code, value
  end
end
iain
  • 16,204
  • 4
  • 37
  • 41
  • Thanks for pointing me in the write direction. However, as you can tell from my answer, I need to 'transpose' the user attributes onto the answers table. Sorry if i didn't make this more clear in my question. – Darragh Dec 15 '10 at 23:21
0

I've come up with the following answer to my question...thanks to the previous poster (iain) who set me in the right direction.

class User < ActiveRecord::Base
  has_many :answers

  Question.all.each do |q|
    define_method q.code do
      a = answers.find_by_question_id(q.id)
      a && a.value || q.default_value
    end
    define_method "#{q.code}=" do |value|
      a = answers.find_by_question_id(q.id) || answers.new({:question_id => q.id})
      a.value = value
      a.save
    end
  end

The only issue I have with this solution is that if I'm showing a user with all answers, there is a separate database query for each question. I would have thought the :answers collection would get loaded only once...

Darragh
  • 2,526
  • 1
  • 23
  • 31