surveyor 0.14.4 → 0.14.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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",