surveyor 0.14.4 → 0.14.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/README.md +7 -2
  2. data/VERSION +1 -1
  3. data/app/models/answer.rb +2 -18
  4. data/app/models/dependency.rb +2 -45
  5. data/app/models/dependency_condition.rb +2 -42
  6. data/app/models/question.rb +2 -49
  7. data/app/models/question_group.rb +2 -27
  8. data/app/models/response.rb +2 -34
  9. data/app/models/response_set.rb +3 -222
  10. data/app/models/survey.rb +2 -55
  11. data/app/models/survey_section.rb +2 -12
  12. data/app/models/validation.rb +2 -32
  13. data/app/models/validation_condition.rb +2 -42
  14. data/generators/extend_surveyor/templates/EXTENDING_SURVEYOR +1 -0
  15. data/lib/surveyor/common.rb +1 -0
  16. data/lib/surveyor/models/answer_methods.rb +25 -0
  17. data/lib/surveyor/models/dependency_condition_methods.rb +47 -0
  18. data/lib/surveyor/models/dependency_methods.rb +52 -0
  19. data/lib/surveyor/models/question_group_methods.rb +35 -0
  20. data/lib/surveyor/models/question_methods.rb +55 -0
  21. data/lib/surveyor/models/response_methods.rb +40 -0
  22. data/lib/surveyor/models/response_set_methods.rb +174 -0
  23. data/lib/surveyor/models/survey_methods.rb +63 -0
  24. data/lib/surveyor/models/survey_section_methods.rb +21 -0
  25. data/lib/surveyor/models/validation_condition_methods.rb +48 -0
  26. data/lib/surveyor/models/validation_methods.rb +40 -0
  27. data/lib/surveyor.rb +3 -3
  28. data/rails/init.rb +1 -1
  29. data/surveyor.gemspec +13 -5
  30. metadata +15 -7
  31. data/generators/extend_surveyor/templates/extensions/survey_extensions.rb +0 -24
  32. data/generators/extend_surveyor/templates/extensions/surveyor_controller_extensions.rb +0 -28
  33. data/generators/extend_surveyor/templates/extensions/surveyor_helper_extensions.rb +0 -17
@@ -1,6 +1,7 @@
1
1
  module Surveyor
2
2
  class Common
3
3
  RAND_CHARS = [('a'..'z'), ('A'..'Z'), (0..9)].map{|r| r.to_a}.flatten.to_s
4
+ OPERATORS = %w(== != < > <= >=)
4
5
 
5
6
  class << self
6
7
  def make_tiny_code(len = 10)
@@ -0,0 +1,25 @@
1
+ module Surveyor
2
+ module Models
3
+ module AnswerMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :question
7
+ base.send :has_many, :responses
8
+ base.send :has_many, :validations, :dependent => :destroy
9
+
10
+ # Scopes
11
+ base.send :default_scope, :order => "display_order ASC"
12
+
13
+ # Validations
14
+ base.send :validates_presence_of, :text
15
+ base.send :validates_numericality_of, :question_id, :allow_nil => false, :only_integer => true
16
+ end
17
+
18
+ # Instance Methods
19
+ def renderer(q = question)
20
+ r = [q.pick.to_s, self.response_class].compact.map(&:downcase).join("_")
21
+ r.blank? ? :default : r.to_sym
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module Surveyor
2
+ module Models
3
+ module DependencyConditionMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :answer
7
+ base.send :belongs_to, :dependency
8
+ base.send :belongs_to, :dependent_question, :foreign_key => :question_id, :class_name => :question
9
+ base.send :belongs_to, :question
10
+
11
+ # Validations
12
+ base.send :validates_numericality_of, :dependency_id, :question_id, :answer_id
13
+ base.send :validates_presence_of, :operator, :rule_key
14
+ base.send :validates_inclusion_of, :operator, :in => Surveyor::Common::OPERATORS
15
+ base.send :validates_uniqueness_of, :rule_key, :scope => :dependency_id
16
+
17
+ base.send :acts_as_response # includes "as" instance method
18
+
19
+ # Class methods
20
+ base.instance_eval do
21
+ def operators
22
+ Surveyor::Common::OPERATORS
23
+ end
24
+ end
25
+ end
26
+
27
+ # Instance methods
28
+ def to_hash(response_set)
29
+ response = response_set.responses.detect{|r| r.answer_id.to_i == self.answer_id.to_i} || false # eval("nil and false") => nil so return false if no response is found
30
+ {rule_key.to_sym => (response and self.is_met?(response))}
31
+ end
32
+
33
+ # Checks to see if the response passed in meets the dependency condition
34
+ def is_met?(response)
35
+ klass = response.answer.response_class
36
+ return case self.operator
37
+ when "==", "<", ">", "<=", ">="
38
+ response.as(klass).send(self.operator, self.as(klass))
39
+ when "!="
40
+ !(response.as(klass) == self.as(klass))
41
+ else
42
+ false
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,52 @@
1
+ module Surveyor
2
+ module Models
3
+ module DependencyMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :question
7
+ base.send :belongs_to, :question_group
8
+ base.send :has_many, :dependency_conditions, :dependent => :destroy
9
+
10
+ # Validations
11
+ base.send :validates_presence_of, :rule
12
+ base.send :validates_format_of, :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/ #TODO properly formed parenthesis etc.
13
+ base.send :validates_numericality_of, :question_id, :if => Proc.new { |d| d.question_group_id.nil? }
14
+ base.send :validates_numericality_of, :question_group_id, :if => Proc.new { |d| d.question_id.nil? }
15
+
16
+ # Attribute aliases
17
+ base.send :alias_attribute, :dependent_question_id, :question_id
18
+ end
19
+
20
+ # Instance Methods
21
+ def question_group_id=(i)
22
+ write_attribute(:question_id, nil) unless i.nil?
23
+ write_attribute(:question_group_id, i)
24
+ end
25
+
26
+ def question_id=(i)
27
+ write_attribute(:question_group_id, nil) unless i.nil?
28
+ write_attribute(:question_id, i)
29
+ end
30
+
31
+ # Has this dependency has been met in the context of response_set?
32
+ # Substitutes the conditions hash into the rule and evaluates it
33
+ def is_met?(response_set)
34
+ ch = conditions_hash(response_set)
35
+ return false if ch.blank?
36
+ # logger.debug "rule: #{self.rule.inspect}"
37
+ # logger.debug "rexp: #{rgx.inspect}"
38
+ # logger.debug "keyp: #{ch.inspect}"
39
+ # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
40
+ rgx = Regexp.new(self.dependency_conditions.map{|dc| ["a","o"].include?(dc.rule_key) ? "#{dc.rule_key}(?!nd|r)" : dc.rule_key}.join("|")) # exclude and, or
41
+ eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
42
+ end
43
+
44
+ # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
45
+ def conditions_hash(response_set)
46
+ hash = {}
47
+ self.dependency_conditions.each{|dc| hash.merge!(dc.to_hash(response_set))}
48
+ return hash
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ module Surveyor
2
+ module Models
3
+ module QuestionGroupMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :has_many, :questions
7
+ base.send :has_one, :dependency
8
+ end
9
+
10
+ # Instance Methods
11
+ def initialize(*args)
12
+ super(*args)
13
+ default_args
14
+ end
15
+
16
+ def default_args
17
+ self.display_type ||= "inline"
18
+ end
19
+
20
+ def renderer
21
+ display_type.blank? ? :default : display_type.to_sym
22
+ end
23
+
24
+ def dependent?
25
+ self.dependency != nil
26
+ end
27
+ def triggered?(response_set)
28
+ dependent? ? self.dependency.is_met?(response_set) : true
29
+ end
30
+ def css_class(response_set)
31
+ [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,55 @@
1
+ module Surveyor
2
+ module Models
3
+ module QuestionMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :survey_section
7
+ base.send :belongs_to, :question_group, :dependent => :destroy
8
+ base.send :has_many, :answers, :order => "display_order ASC", :dependent => :destroy # it might not always have answers
9
+ base.send :has_one, :dependency, :dependent => :destroy
10
+
11
+ # Scopes
12
+ base.send :default_scope, :order => "display_order ASC"
13
+
14
+ # Validations
15
+ base.send :validates_presence_of, :text, :survey_section_id, :display_order
16
+ base.send :validates_inclusion_of, :is_mandatory, :in => [true, false]
17
+ end
18
+
19
+ # Instance Methods
20
+ def initialize(*args)
21
+ super(*args)
22
+ default_args
23
+ end
24
+
25
+ def default_args
26
+ self.is_mandatory ||= true
27
+ self.display_type ||= "default"
28
+ self.pick ||= "none"
29
+ end
30
+
31
+ def mandatory?
32
+ self.is_mandatory == true
33
+ end
34
+
35
+ def dependent?
36
+ self.dependency != nil
37
+ end
38
+ def triggered?(response_set)
39
+ dependent? ? self.dependency.is_met?(response_set) : true
40
+ end
41
+ def css_class(response_set)
42
+ [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
43
+ end
44
+
45
+ def part_of_group?
46
+ !self.question_group.nil?
47
+ end
48
+
49
+ def renderer(g = question_group)
50
+ r = [g ? g.renderer.to_s : nil, display_type].compact.join("_")
51
+ r.blank? ? :default : r.to_sym
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,40 @@
1
+ module Surveyor
2
+ module Models
3
+ module ResponseMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :response_set
7
+ base.send :belongs_to, :question
8
+ base.send :belongs_to, :answer
9
+
10
+ # Validations
11
+ base.send :validates_presence_of, :response_set_id, :question_id, :answer_id
12
+
13
+ base.send :acts_as_response # includes "as" instance method
14
+ end
15
+
16
+ # Instance Methods
17
+ def selected
18
+ !self.new_record?
19
+ end
20
+
21
+ alias_method :selected?, :selected
22
+
23
+ def selected=(value)
24
+ true
25
+ end
26
+
27
+ def correct?
28
+ question.correct_answer_id.nil? or self.answer.response_class != "answer" or (question.correct_answer_id.to_i == answer_id.to_i)
29
+ end
30
+
31
+ def to_s # used in dependency_explanation_helper
32
+ if self.answer.response_class == "answer" and self.answer_id
33
+ return self.answer.text
34
+ else
35
+ return "#{(self.string_value || self.text_value || self.integer_value || self.float_value || nil).to_s}"
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,174 @@
1
+ module Surveyor
2
+ module Models
3
+ module ResponseSetMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :survey
7
+ base.send :belongs_to, :user
8
+ base.send :has_many, :responses, :dependent => :destroy
9
+
10
+ # Validations
11
+ base.send :validates_presence_of, :survey_id
12
+ base.send :validates_associated, :responses
13
+ base.send :validates_uniqueness_of, :access_code
14
+
15
+ # Attributes
16
+ base.send :attr_protected, :completed_at
17
+ base.send :attr_accessor, :current_section_id
18
+
19
+ # Callbacks
20
+ base.send :after_update, :save_responses
21
+ end
22
+
23
+ # Instance methods
24
+ def initialize(*args)
25
+ super(*args)
26
+ default_args
27
+ end
28
+
29
+ def default_args
30
+ self.started_at ||= Time.now
31
+ self.access_code = Surveyor::Common.make_tiny_code
32
+ end
33
+
34
+ def access_code=(val)
35
+ while ResponseSet.find_by_access_code(val)
36
+ val = Surveyor::Common.make_tiny_code
37
+ end
38
+ super
39
+ end
40
+
41
+ def to_csv
42
+ qcols = Question.content_columns.map(&:name) - %w(created_at updated_at)
43
+ acols = Answer.content_columns.map(&:name) - %w(created_at updated_at)
44
+ rcols = Response.content_columns.map(&:name)
45
+ require 'fastercsv'
46
+ FCSV(result = "") do |csv|
47
+ csv << qcols.map{|qcol| "question.#{qcol}"} + acols.map{|acol| "answer.#{acol}"} + rcols.map{|rcol| "response.#{rcol}"}
48
+ responses.each do |response|
49
+ csv << qcols.map{|qcol| response.question.send(qcol)} + acols.map{|acol| response.answer.send(acol)} + rcols.map{|rcol| response.send(rcol)}
50
+ end
51
+ end
52
+ result
53
+ end
54
+
55
+ def response_for(question_id, answer_id, group = nil)
56
+ found = responses.detect{|r| r.question_id == question_id && r.answer_id == answer_id && r.response_group.to_s == group.to_s}
57
+ found.blank? ? responses.new(:question_id => question_id, :answer_id => answer_id, :response_group => group) : found
58
+ end
59
+
60
+ def clear_responses
61
+ question_ids = Question.find_all_by_survey_section_id(current_section_id).map(&:id)
62
+ responses.select{|r| question_ids.include? r.question_id}.map(&:destroy)
63
+ responses.reload
64
+ end
65
+
66
+ def response_attributes=(response_attributes)
67
+ response_attributes.each do |question_id, responses_hash|
68
+ # Response.delete_all(["response_set_id =? AND question_id =?", self.id, question_id])
69
+ if (answer_id = responses_hash[:answer_id])
70
+ if (!responses_hash[:answer_id].empty?) # Dropdowns return answer id but have an empty value if they are not set... ignoring those.
71
+ #radio or dropdown - only one response
72
+ responses.build({:question_id => question_id, :answer_id => answer_id, :survey_section_id => current_section_id}.merge(responses_hash[answer_id] || {}))
73
+ end
74
+ else
75
+ #possibly multiples responses - unresponded radios end up here too
76
+ # we use the variable question_id, not the "question_id" in the response_hash
77
+ responses_hash.delete_if{|k,v| k == "question_id"}.each do |answer_id, response_hash|
78
+ unless response_hash.delete_if{|k,v| v.blank?}.empty?
79
+ responses.build({:question_id => question_id, :answer_id => answer_id, :survey_section_id => current_section_id}.merge(response_hash))
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def response_group_attributes=(response_attributes)
87
+ response_attributes.each do |question_id, responses_group_hash|
88
+ # Response.delete_all(["response_set_id =? AND question_id =?", self.id, question_id])
89
+ responses_group_hash.each do |response_group_number, group_hash|
90
+ if (answer_id = group_hash[:answer_id]) # if group_hash has an answer_id key we treat it differently
91
+ if (!group_hash[:answer_id].empty?) # dropdowns return empty values in answer_ids if they are not selected
92
+ #radio or dropdown - only one response
93
+ responses.build({:question_id => question_id, :answer_id => answer_id, :response_group => response_group_number, :survey_section_id => current_section_id}.merge(group_hash[answer_id] || {}))
94
+ end
95
+ else
96
+ #possibly multiples responses - unresponded radios end up here too
97
+ # we use the variable question_id in the key, not the "question_id" in the response_hash... same with response_group key
98
+ group_hash.delete_if{|k,v| (k == "question_id") or (k == "response_group")}.each do |answer_id, inner_hash|
99
+ unless inner_hash.delete_if{|k,v| v.blank?}.empty?
100
+ responses.build({:question_id => question_id, :answer_id => answer_id, :response_group => response_group_number, :survey_section_id => current_section_id}.merge(inner_hash))
101
+ end
102
+ end
103
+ end
104
+
105
+ end
106
+ end
107
+ end
108
+
109
+ def save_responses
110
+ responses.each{|response| response.save(false)}
111
+ end
112
+
113
+ def complete!
114
+ self.completed_at = Time.now
115
+ end
116
+
117
+ def correct?
118
+ responses.all?(&:correct?)
119
+ end
120
+ def correctness_hash
121
+ { :questions => survey.sections_with_questions.map(&:questions).flatten.compact.size,
122
+ :responses => responses.compact.size,
123
+ :correct => responses.find_all(&:correct?).compact.size
124
+ }
125
+ end
126
+ def mandatory_questions_complete?
127
+ progress_hash[:triggered_mandatory] == progress_hash[:triggered_mandatory_completed]
128
+ end
129
+ def progress_hash
130
+ qs = survey.sections_with_questions.map(&:questions).flatten
131
+ ds = dependencies(qs.map(&:id))
132
+ triggered = qs - ds.select{|d| !d.is_met?(self)}.map(&:question)
133
+ { :questions => qs.compact.size,
134
+ :triggered => triggered.compact.size,
135
+ :triggered_mandatory => triggered.select{|q| q.mandatory?}.compact.size,
136
+ :triggered_mandatory_completed => triggered.select{|q| q.mandatory? and is_answered?(q)}.compact.size
137
+ }
138
+ end
139
+ def is_answered?(question)
140
+ %w(label image).include?(question.display_type) or !is_unanswered?(question)
141
+ end
142
+ def is_unanswered?(question)
143
+ self.responses.detect{|r| r.question_id == question.id}.nil?
144
+ end
145
+
146
+ # Returns the number of response groups (count of group responses enterted) for this question group
147
+ def count_group_responses(questions)
148
+ questions.map{|q| responses.select{|r| (r.question_id.to_i == q.id.to_i) && !r.response_group.nil?}.group_by(&:response_group).size }.max
149
+ end
150
+
151
+ def unanswered_dependencies
152
+ dependencies.select{|d| d.is_met?(self) and self.is_unanswered?(d.question)}.map(&:question)
153
+ end
154
+
155
+ def all_dependencies
156
+ arr = dependencies.partition{|d| d.is_met?(self) }
157
+ {:show => arr[0].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group_#{d.question_group_id}"}, :hide => arr[1].map{|d| d.question_group_id.nil? ? "question_#{d.question_id}" : "question_group_#{d.question_group_id}"}}
158
+ end
159
+
160
+ # Check existence of responses to questions from a given survey_section
161
+ def no_responses_for_section?(section)
162
+ self.responses.count(:conditions => {:survey_section_id => section.id}) == 0
163
+ end
164
+
165
+ protected
166
+
167
+ def dependencies(question_ids = nil)
168
+ question_ids ||= Question.find_all_by_survey_section_id(current_section_id).map(&:id)
169
+ depdendecy_ids = DependencyCondition.all(:conditions => {:question_id => question_ids}).map(&:dependency_id)
170
+ Dependency.find(depdendecy_ids, :include => :dependency_conditions)
171
+ end
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,63 @@
1
+ module Surveyor
2
+ module Models
3
+ module SurveyMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :has_many, :sections, :class_name => "SurveySection", :order => 'display_order'
7
+ base.send :has_many, :sections_with_questions, :include => :questions, :class_name => "SurveySection", :order => 'display_order'
8
+ base.send :has_many, :response_sets
9
+
10
+ # Scopes
11
+ base.send :named_scope, :with_sections, {:include => :sections}
12
+
13
+ # Validations
14
+ base.send :validates_presence_of, :title
15
+ base.send :validates_uniqueness_of, :access_code
16
+
17
+ # Class methods
18
+ base.instance_eval do
19
+ def to_normalized_string(value)
20
+ # replace non-alphanumeric with "-". remove repeat "-"s. don't start or end with "-"
21
+ value.to_s.downcase.gsub(/[^a-z0-9]/,"-").gsub(/-+/,"-").gsub(/-$|^-/,"")
22
+ end
23
+ end
24
+ end
25
+
26
+ # Instance methods
27
+ def initialize(*args)
28
+ super(*args)
29
+ default_args
30
+ end
31
+
32
+ def default_args
33
+ self.inactive_at ||= DateTime.now
34
+ end
35
+
36
+ def title=(value)
37
+ self.access_code = Surveyor::Common.to_normalized_string(value)
38
+ super
39
+ end
40
+
41
+ def active?
42
+ self.active_as_of?(DateTime.now)
43
+ end
44
+ def active_as_of?(datetime)
45
+ (self.active_at.nil? or self.active_at < datetime) and (self.inactive_at.nil? or self.inactive_at > datetime)
46
+ end
47
+ def activate!
48
+ self.active_at = DateTime.now
49
+ end
50
+ def deactivate!
51
+ self.inactive_at = DateTime.now
52
+ end
53
+ def active_at=(datetime)
54
+ self.inactive_at = nil if !datetime.nil? and !self.inactive_at.nil? and self.inactive_at < datetime
55
+ super(datetime)
56
+ end
57
+ def inactive_at=(datetime)
58
+ self.active_at = nil if !datetime.nil? and !self.active_at.nil? and self.active_at > datetime
59
+ super(datetime)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Surveyor
2
+ module Models
3
+ module SurveySectionMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :has_many, :questions, :order => "display_order ASC", :dependent => :destroy
7
+ base.send :belongs_to, :survey
8
+
9
+ # Scopes
10
+ base.send :default_scope, :order => "display_order ASC"
11
+ base.send :named_scope, :with_includes, { :include => {:questions => [:answers, :question_group, {:dependency => :dependency_conditions}]}}
12
+
13
+ # Validations
14
+ base.send :validates_presence_of, :title, :survey, :display_order
15
+ end
16
+
17
+ # Instance Methods
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ module Surveyor
2
+ module Models
3
+ module ValidationConditionMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :validation
7
+
8
+ # Scopes
9
+
10
+ # Validations
11
+ base.send :validates_numericality_of, :validation_id #, :question_id, :answer_id
12
+ base.send :validates_presence_of, :operator, :rule_key
13
+ base.send :validates_inclusion_of, :operator, :in => Surveyor::Common::OPERATORS
14
+ base.send :validates_uniqueness_of, :rule_key, :scope => :validation_id
15
+
16
+ base.send :acts_as_response # includes "as" instance method
17
+
18
+ # Class methods
19
+ base.instance_eval do
20
+ def operators
21
+ Surveyor::Common::OPERATORS
22
+ end
23
+ end
24
+ end
25
+
26
+ # Instance Methods
27
+ def to_hash(response)
28
+ {rule_key.to_sym => (response.nil? ? false : self.is_valid?(response))}
29
+ end
30
+
31
+ def is_valid?(response)
32
+ klass = response.answer.response_class
33
+ compare_to = Response.find_by_question_id_and_answer_id(self.question_id, self.answer_id) || self
34
+ case self.operator
35
+ when "==", "<", ">", "<=", ">="
36
+ response.as(klass).send(self.operator, compare_to.as(klass))
37
+ when "!="
38
+ !(response.as(klass) == compare_to.as(klass))
39
+ when "=~"
40
+ return false if compare_to != self
41
+ !(response.as(klass).to_s =~ Regexp.new(self.regexp || "")).nil?
42
+ else
43
+ false
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module Surveyor
2
+ module Models
3
+ module ValidationMethods
4
+ def self.included(base)
5
+ # Associations
6
+ base.send :belongs_to, :answer
7
+ base.send :has_many, :validation_conditions, :dependent => :destroy
8
+
9
+ # Scopes
10
+
11
+ # Validations
12
+ base.send :validates_presence_of, :rule
13
+ base.send :validates_format_of, :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/
14
+ base.send :validates_numericality_of, :answer_id
15
+
16
+ end
17
+
18
+ # Instance Methods
19
+ def is_valid?(response_set)
20
+ ch = conditions_hash(response_set)
21
+ rgx = Regexp.new(self.validation_conditions.map{|vc| ["a","o"].include?(vc.rule_key) ? "#{vc.rule_key}(?!nd|r)" : vc.rule_key}.join("|")) # exclude and, or
22
+ # logger.debug "v: #{self.inspect}"
23
+ # logger.debug "rule: #{self.rule.inspect}"
24
+ # logger.debug "rexp: #{rgx.inspect}"
25
+ # logger.debug "keyp: #{ch.inspect}"
26
+ # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
27
+ eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
28
+ end
29
+
30
+ # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
31
+ def conditions_hash(response_set)
32
+ hash = {}
33
+ response = response_set.responses.detect{|r| r.answer_id.to_i == self.answer_id.to_i}
34
+ # logger.debug "r: #{response.inspect}"
35
+ self.validation_conditions.each{|vc| hash.merge!(vc.to_hash(response))}
36
+ return hash
37
+ end
38
+ end
39
+ end
40
+ end
data/lib/surveyor.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'surveyor/common'
2
2
  require 'surveyor/acts_as_response'
3
3
 
4
-
5
- Dir.glob(File.join(File.dirname(__FILE__),'..','app','models','*.rb')).each{|f| require f}
6
- Dir.glob(File.join(File.dirname(__FILE__),'..','app','helpers','*.rb')).each{|f| require f}
4
+ #
5
+ # Dir.glob(File.join(File.dirname(__FILE__),'..','app','models','*.rb')).each{|f| require f}
6
+ # Dir.glob(File.join(File.dirname(__FILE__),'..','app','helpers','*.rb')).each{|f| require f}
data/rails/init.rb CHANGED
@@ -1 +1 @@
1
- # Init hook code here
1
+ require 'surveyor'
data/surveyor.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{surveyor}
8
- s.version = "0.14.4"
8
+ s.version = "0.14.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Chamberlain", "Mark Yoon"]
12
- s.date = %q{2010-08-31}
12
+ s.date = %q{2010-09-01}
13
13
  s.email = %q{yoon@northwestern.edu}
14
14
  s.extra_rdoc_files = [
15
15
  "README.md"
@@ -56,11 +56,8 @@ Gem::Specification.new do |s|
56
56
  "features/surveyor.feature",
57
57
  "generators/extend_surveyor/extend_surveyor_generator.rb",
58
58
  "generators/extend_surveyor/templates/EXTENDING_SURVEYOR",
59
- "generators/extend_surveyor/templates/extensions/survey_extensions.rb",
60
59
  "generators/extend_surveyor/templates/extensions/surveyor_controller.rb",
61
- "generators/extend_surveyor/templates/extensions/surveyor_controller_extensions.rb",
62
60
  "generators/extend_surveyor/templates/extensions/surveyor_custom.html.erb",
63
- "generators/extend_surveyor/templates/extensions/surveyor_helper_extensions.rb",
64
61
  "generators/surveyor/surveyor_generator.rb",
65
62
  "generators/surveyor/templates/README",
66
63
  "generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_leftright.gif",
@@ -145,6 +142,17 @@ Gem::Specification.new do |s|
145
142
  "lib/surveyor.rb",
146
143
  "lib/surveyor/acts_as_response.rb",
147
144
  "lib/surveyor/common.rb",
145
+ "lib/surveyor/models/answer_methods.rb",
146
+ "lib/surveyor/models/dependency_condition_methods.rb",
147
+ "lib/surveyor/models/dependency_methods.rb",
148
+ "lib/surveyor/models/question_group_methods.rb",
149
+ "lib/surveyor/models/question_methods.rb",
150
+ "lib/surveyor/models/response_methods.rb",
151
+ "lib/surveyor/models/response_set_methods.rb",
152
+ "lib/surveyor/models/survey_methods.rb",
153
+ "lib/surveyor/models/survey_section_methods.rb",
154
+ "lib/surveyor/models/validation_condition_methods.rb",
155
+ "lib/surveyor/models/validation_methods.rb",
148
156
  "lib/surveyor/surveyor_controller_methods.rb",
149
157
  "lib/tasks/surveyor_tasks.rake",
150
158
  "rails/init.rb",