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
data/README.md CHANGED
@@ -102,7 +102,7 @@ Surveyor's controller, models, and views may be customized via classes in your a
102
102
 
103
103
  script/generate extend_surveyor
104
104
 
105
- and check out surveys/README\_FOR\_CUSTOM\_SURVEYOR.md
105
+ and check out surveys/EXTENDING\_SURVEYOR
106
106
 
107
107
  # Dependencices
108
108
 
@@ -114,9 +114,14 @@ To work on the plugin code (for enhancements, and bug fixes, etc...) fork this g
114
114
 
115
115
  # Changes
116
116
 
117
+ 0.14.5
118
+
119
+ * use modules to include model methods. re-closes #77
120
+ * rails init. destroy dependent models
121
+
117
122
  0.14.4
118
123
 
119
- * explicity require surveyor models and helper. update sweeper syntax. closes #77
124
+ * explicitly require surveyor models and helper. update sweeper syntax. closes #77
120
125
  * cleanup and requires
121
126
  * fixing instructions for extending surveyor. closes #76
122
127
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.14.4
1
+ 0.14.5
data/app/models/answer.rb CHANGED
@@ -1,20 +1,4 @@
1
1
  class Answer < ActiveRecord::Base
2
-
3
- # Associations
4
- belongs_to :question
5
- has_many :responses
6
-
7
- # Scopes
8
- default_scope :order => "display_order ASC"
9
-
10
- # Validations
11
- validates_presence_of :text
12
- validates_numericality_of :question_id, :allow_nil => false, :only_integer => true
13
-
14
- # Methods
15
- def renderer(q = question)
16
- r = [q.pick.to_s, self.response_class].compact.map(&:downcase).join("_")
17
- r.blank? ? :default : r.to_sym
18
- end
19
-
2
+ unloadable
3
+ include Surveyor::Models::AnswerMethods
20
4
  end
@@ -1,47 +1,4 @@
1
1
  class Dependency < ActiveRecord::Base
2
-
3
- # Associations
4
- belongs_to :question
5
- belongs_to :question_group
6
- has_many :dependency_conditions
7
-
8
- # Validations
9
- validates_presence_of :rule
10
- validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/ #TODO properly formed parenthesis etc.
11
- validates_numericality_of :question_id, :if => Proc.new { |d| d.question_group_id.nil? }
12
- validates_numericality_of :question_group_id, :if => Proc.new { |d| d.question_id.nil? }
13
-
14
- # Attribute aliases
15
- alias_attribute :dependent_question_id, :question_id
16
-
17
- def question_group_id=(i)
18
- write_attribute(:question_id, nil) unless i.nil?
19
- write_attribute(:question_group_id, i)
20
- end
21
-
22
- def question_id=(i)
23
- write_attribute(:question_group_id, nil) unless i.nil?
24
- write_attribute(:question_id, i)
25
- end
26
-
27
- # Has this dependency has been met in the context of response_set?
28
- # Substitutes the conditions hash into the rule and evaluates it
29
- def is_met?(response_set)
30
- ch = conditions_hash(response_set)
31
- return false if ch.blank?
32
- # logger.debug "rule: #{self.rule.inspect}"
33
- # logger.debug "rexp: #{rgx.inspect}"
34
- # logger.debug "keyp: #{ch.inspect}"
35
- # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
36
- 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
37
- eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
38
- end
39
-
40
- # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
41
- def conditions_hash(response_set)
42
- hash = {}
43
- self.dependency_conditions.each{|dc| hash.merge!(dc.to_hash(response_set))}
44
- return hash
45
- end
46
-
2
+ unloadable
3
+ include Surveyor::Models::DependencyMethods
47
4
  end
@@ -1,44 +1,4 @@
1
1
  class DependencyCondition < ActiveRecord::Base
2
-
3
- # Constants
4
- OPERATORS = %w(== != < > <= >=) # CONSTANT or @@class_variable when validations listed before class method
5
-
6
- # Associations
7
- belongs_to :answer
8
- belongs_to :dependency
9
- belongs_to :dependent_question, :foreign_key => :question_id, :class_name => :question
10
- belongs_to :question
11
-
12
- # Validations
13
- validates_numericality_of :dependency_id, :question_id, :answer_id
14
- validates_presence_of :operator, :rule_key
15
- validates_inclusion_of :operator, :in => OPERATORS
16
- validates_uniqueness_of :rule_key, :scope => :dependency_id
17
-
18
- acts_as_response # includes "as" instance method
19
-
20
- # Class methods
21
- def self.operators
22
- OPERATORS
23
- end
24
-
25
- # Instance methods
26
- def to_hash(response_set)
27
- 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
28
- {rule_key.to_sym => (response and self.is_met?(response))}
29
- end
30
-
31
- # Checks to see if the response passed in meets the dependency condition
32
- def is_met?(response)
33
- klass = response.answer.response_class
34
- return case self.operator
35
- when "==", "<", ">", "<=", ">="
36
- response.as(klass).send(self.operator, self.as(klass))
37
- when "!="
38
- !(response.as(klass) == self.as(klass))
39
- else
40
- false
41
- end
42
- end
43
-
2
+ unloadable
3
+ include Surveyor::Models::DependencyConditionMethods
44
4
  end
@@ -1,51 +1,4 @@
1
1
  class Question < ActiveRecord::Base
2
-
3
- # Associations
4
- belongs_to :survey_section
5
- belongs_to :question_group
6
- has_many :answers, :order => "display_order ASC" # it might not always have answers
7
- has_one :dependency
8
-
9
- # Scopes
10
- default_scope :order => "display_order ASC"
11
-
12
- # Validations
13
- validates_presence_of :text, :survey_section_id, :display_order
14
- validates_inclusion_of :is_mandatory, :in => [true, false]
15
-
16
- # Instance Methods
17
- def initialize(*args)
18
- super(*args)
19
- default_args
20
- end
21
-
22
- def default_args
23
- self.is_mandatory ||= true
24
- self.display_type ||= "default"
25
- self.pick ||= "none"
26
- end
27
-
28
- def mandatory?
29
- self.is_mandatory == true
30
- end
31
-
32
- def dependent?
33
- self.dependency != nil
34
- end
35
- def triggered?(response_set)
36
- dependent? ? self.dependency.is_met?(response_set) : true
37
- end
38
- def css_class(response_set)
39
- [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
40
- end
41
-
42
- def part_of_group?
43
- !self.question_group.nil?
44
- end
45
-
46
- def renderer(g = question_group)
47
- r = [g ? g.renderer.to_s : nil, display_type].compact.join("_")
48
- r.blank? ? :default : r.to_sym
49
- end
50
-
2
+ unloadable
3
+ include Surveyor::Models::QuestionMethods
51
4
  end
@@ -1,30 +1,5 @@
1
1
  class QuestionGroup < ActiveRecord::Base
2
-
3
- has_many :questions
4
- has_one :dependency
5
-
6
- # Instance Methods
7
- def initialize(*args)
8
- super(*args)
9
- default_args
10
- end
11
-
12
- def default_args
13
- self.display_type ||= "inline"
14
- end
15
-
16
- def renderer
17
- display_type.blank? ? :default : display_type.to_sym
18
- end
19
-
20
- def dependent?
21
- self.dependency != nil
22
- end
23
- def triggered?(response_set)
24
- dependent? ? self.dependency.is_met?(response_set) : true
25
- end
26
- def css_class(response_set)
27
- [(dependent? ? "dependent" : nil), (triggered?(response_set) ? nil : "hidden"), custom_class].compact.join(" ")
28
- end
2
+ unloadable
3
+ include Surveyor::Models::QuestionGroupMethods
29
4
 
30
5
  end
@@ -1,37 +1,5 @@
1
1
  class Response < ActiveRecord::Base
2
-
2
+ unloadable
3
3
  include ActionView::Helpers::SanitizeHelper
4
-
5
- # Associations
6
- belongs_to :response_set
7
- belongs_to :question
8
- belongs_to :answer
9
-
10
- # Validations
11
- validates_presence_of :response_set_id, :question_id, :answer_id
12
-
13
- acts_as_response # includes "as" instance method
14
-
15
- def selected
16
- !self.new_record?
17
- end
18
-
19
- alias_method :selected?, :selected
20
-
21
- def selected=(value)
22
- true
23
- end
24
-
25
- def correct?
26
- question.correct_answer_id.nil? or self.answer.response_class != "answer" or (question.correct_answer_id.to_i == answer_id.to_i)
27
- end
28
-
29
- def to_s # used in dependency_explanation_helper
30
- if self.answer.response_class == "answer" and self.answer_id
31
- return self.answer.text
32
- else
33
- return "#{(self.string_value || self.text_value || self.integer_value || self.float_value || nil).to_s}"
34
- end
35
- end
36
-
4
+ include Surveyor::Models::ResponseMethods
37
5
  end
@@ -1,223 +1,4 @@
1
1
  class ResponseSet < ActiveRecord::Base
2
-
3
- # Associations
4
- belongs_to :survey
5
- belongs_to :user
6
- has_many :responses, :dependent => :destroy
7
-
8
- # Validations
9
- validates_presence_of :survey_id
10
- validates_associated :responses
11
- validates_uniqueness_of :access_code
12
-
13
- # Attributes
14
- attr_protected :completed_at
15
- attr_accessor :current_section_id
16
-
17
- # Callbacks
18
- after_update :save_responses
19
-
20
- # Instance methods
21
- def initialize(*args)
22
- super(*args)
23
- default_args
24
- end
25
-
26
- def default_args
27
- self.started_at ||= Time.now
28
- self.access_code = Surveyor::Common.make_tiny_code
29
- end
30
-
31
- def access_code=(val)
32
- while ResponseSet.find_by_access_code(val)
33
- val = Surveyor::Common.make_tiny_code
34
- end
35
- super
36
- end
37
-
38
- def to_csv
39
- qcols = Question.content_columns.map(&:name) - %w(created_at updated_at)
40
- acols = Answer.content_columns.map(&:name) - %w(created_at updated_at)
41
- rcols = Response.content_columns.map(&:name)
42
- require 'fastercsv'
43
- FCSV(result = "") do |csv|
44
- csv << qcols.map{|qcol| "question.#{qcol}"} + acols.map{|acol| "answer.#{acol}"} + rcols.map{|rcol| "response.#{rcol}"}
45
- responses.each do |response|
46
- csv << qcols.map{|qcol| response.question.send(qcol)} + acols.map{|acol| response.answer.send(acol)} + rcols.map{|rcol| response.send(rcol)}
47
- end
48
- end
49
- result
50
- end
51
-
52
- def response_for(question_id, answer_id, group = nil)
53
- found = responses.detect{|r| r.question_id == question_id && r.answer_id == answer_id && r.response_group.to_s == group.to_s}
54
- found.blank? ? responses.new(:question_id => question_id, :answer_id => answer_id, :response_group => group) : found
55
- end
56
-
57
- def clear_responses
58
- question_ids = Question.find_all_by_survey_section_id(current_section_id).map(&:id)
59
- responses.select{|r| question_ids.include? r.question_id}.map(&:destroy)
60
- responses.reload
61
- end
62
-
63
- def response_attributes=(response_attributes)
64
- response_attributes.each do |question_id, responses_hash|
65
- # Response.delete_all(["response_set_id =? AND question_id =?", self.id, question_id])
66
- if (answer_id = responses_hash[:answer_id])
67
- if (!responses_hash[:answer_id].empty?) # Dropdowns return answer id but have an empty value if they are not set... ignoring those.
68
- #radio or dropdown - only one response
69
- responses.build({:question_id => question_id, :answer_id => answer_id, :survey_section_id => current_section_id}.merge(responses_hash[answer_id] || {}))
70
- end
71
- else
72
- #possibly multiples responses - unresponded radios end up here too
73
- # we use the variable question_id, not the "question_id" in the response_hash
74
- responses_hash.delete_if{|k,v| k == "question_id"}.each do |answer_id, response_hash|
75
- unless response_hash.delete_if{|k,v| v.blank?}.empty?
76
- responses.build({:question_id => question_id, :answer_id => answer_id, :survey_section_id => current_section_id}.merge(response_hash))
77
- end
78
- end
79
- end
80
- end
81
- end
82
-
83
- def response_group_attributes=(response_attributes)
84
- response_attributes.each do |question_id, responses_group_hash|
85
- # Response.delete_all(["response_set_id =? AND question_id =?", self.id, question_id])
86
- responses_group_hash.each do |response_group_number, group_hash|
87
- if (answer_id = group_hash[:answer_id]) # if group_hash has an answer_id key we treat it differently
88
- if (!group_hash[:answer_id].empty?) # dropdowns return empty values in answer_ids if they are not selected
89
- #radio or dropdown - only one response
90
- 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] || {}))
91
- end
92
- else
93
- #possibly multiples responses - unresponded radios end up here too
94
- # we use the variable question_id in the key, not the "question_id" in the response_hash... same with response_group key
95
- group_hash.delete_if{|k,v| (k == "question_id") or (k == "response_group")}.each do |answer_id, inner_hash|
96
- unless inner_hash.delete_if{|k,v| v.blank?}.empty?
97
- responses.build({:question_id => question_id, :answer_id => answer_id, :response_group => response_group_number, :survey_section_id => current_section_id}.merge(inner_hash))
98
- end
99
- end
100
- end
101
-
102
- end
103
- end
104
- end
105
-
106
- def save_responses
107
- responses.each{|response| response.save(false)}
108
- end
109
-
110
- def complete!
111
- self.completed_at = Time.now
112
- end
113
-
114
- def correct?
115
- responses.all?(&:correct?)
116
- end
117
- def correctness_hash
118
- { :questions => survey.sections_with_questions.map(&:questions).flatten.compact.size,
119
- :responses => responses.compact.size,
120
- :correct => responses.find_all(&:correct?).compact.size
121
- }
122
- end
123
- def mandatory_questions_complete?
124
- progress_hash[:triggered_mandatory] == progress_hash[:triggered_mandatory_completed]
125
- end
126
- def progress_hash
127
- qs = survey.sections_with_questions.map(&:questions).flatten
128
- ds = dependencies(qs.map(&:id))
129
- triggered = qs - ds.select{|d| !d.is_met?(self)}.map(&:question)
130
- { :questions => qs.compact.size,
131
- :triggered => triggered.compact.size,
132
- :triggered_mandatory => triggered.select{|q| q.mandatory?}.compact.size,
133
- :triggered_mandatory_completed => triggered.select{|q| q.mandatory? and is_answered?(q)}.compact.size
134
- }
135
- end
136
- def is_answered?(question)
137
- %w(label image).include?(question.display_type) or !is_unanswered?(question)
138
- end
139
- def is_unanswered?(question)
140
- self.responses.detect{|r| r.question_id == question.id}.nil?
141
- end
142
-
143
- # Returns the number of response groups (count of group responses enterted) for this question group
144
- def count_group_responses(questions)
145
- 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
146
- end
147
-
148
- def unanswered_dependencies
149
- dependencies.select{|d| d.is_met?(self) and self.is_unanswered?(d.question)}.map(&:question)
150
- end
151
-
152
- def all_dependencies
153
- arr = dependencies.partition{|d| d.is_met?(self) }
154
- {: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}"}}
155
- end
156
-
157
- # Check existence of responses to questions from a given survey_section
158
- def no_responses_for_section?(section)
159
- self.responses.count(:conditions => {:survey_section_id => section.id}) == 0
160
- end
161
-
162
- protected
163
-
164
- def dependencies(question_ids = nil)
165
- question_ids ||= Question.find_all_by_survey_section_id(current_section_id).map(&:id)
166
- depdendecy_ids = DependencyCondition.all(:conditions => {:question_id => question_ids}).map(&:dependency_id)
167
- Dependency.find(depdendecy_ids, :include => :dependency_conditions)
168
- end
169
-
170
- end
171
-
172
- # responses
173
-
174
- # "responses"=>{
175
- #string "6"=>{"question_id"=>"6", "20"=>{"string_value"=>"saf"}},
176
- #text "7"=>{"question_id"=>"7", "21"=>{"text_value"=>""}},
177
- #radio+txt "1"=>{"question_id"=>"1", "answer_id"=>"1", "4"=>{"string_value"=>""}},
178
- #radio "2"=>{"answer_id"=>"6"},
179
- #radio "3"=>{"answer_id"=>"10"},
180
- #check "4"=>{"question_id"=>"4", "answer_id"=>"15"},
181
- #check+txt "5"=>{"question_id"=>"5", "16"=>{"selected"=>"1"}, "19"=>{"string_value"=>""}}
182
- # },
183
- # "survey_code"=>"test_survey",
184
- # "commit"=>"Next Section (Utensiles and you!) >>",
185
- # "authenticity_token"=>"8bee21081eea820ab1c658358c0baaa2e46de5d1",
186
- # "_method"=>"put",
187
- # "action"=>"update",
188
- # "controller"=>"app",
189
- # "response_set_code"=>"T2x8HhCQej",
190
- # "section"=>"2"
191
-
192
- # response groups
193
-
194
- # "24"=>{
195
- # "0"=>{"response_group"=>"0", "question_id"=>"24", "answer_id"=>"172"}, "1"=>{"response_group"=>"1", "question_id"=>"24", "answer_id"=>"173"},
196
- # "2"=>{"response_group"=>"2", "question_id"=>"24", "answer_id"=>""}, "3"=>{"response_group"=>"3", "question_id"=>"24", "answer_id"=>""},
197
- # "4"=>{"response_group"=>"4", "question_id"=>"24", "answer_id"=>""}},
198
- # where "24" is the question id
199
-
200
- # Some other examples:
201
- # "25"=>{
202
- # "0"=>{"response_group"=>"0", "question_id"=>"25", "179"=>{"string_value"=>"camry"}},
203
- # "1"=>{"response_group"=>"1", "question_id"=>"25", "179"=>{"string_value"=>"f150"}},
204
- # "2"=>{"response_group"=>"2", "question_id"=>"25", "179"=>{"string_value"=>""}},
205
- # "3"=>{"response_group"=>"3", "question_id"=>"25", "179"=>{"string_value"=>""}},
206
- # "4"=>{"response_group"=>"4", "question_id"=>"25", "179"=>{"string_value"=>""}}},
207
- #
208
- # "26"=>{
209
- # "0"=>{"response_group"=>"0", "question_id"=>"26", "180"=>{"string_value"=>"1999"}},
210
- # "1"=>{"response_group"=>"1", "question_id"=>"26", "180"=>{"string_value"=>"2004"}},
211
- # "2"=>{"response_group"=>"2", "question_id"=>"26", "180"=>{"string_value"=>""}},
212
- # "3"=>{"response_group"=>"3", "question_id"=>"26", "180"=>{"string_value"=>""}},
213
- # "4"=>{"response_group"=>"4", "question_id"=>"26", "180"=>{"string_value"=>""}}},
214
- #
215
- # "27"=>{
216
- # "0"=>{"182"=>{"integer_value"=>""}, "response_group"=>"0", "question_id"=>"27", "181"=>{"string_value"=>""}},
217
- # "1"=>{"182"=>{"integer_value"=>""}, "response_group"=>"1", "question_id"=>"27", "181"=>{"string_value"=>""}},
218
- # "2"=>{"182"=>{"integer_value"=>""}, "response_group"=>"2", "question_id"=>"27", "181"=>{"string_value"=>""}},
219
- # "3"=>{"182"=>{"integer_value"=>""}, "response_group"=>"3", "question_id"=>"27", "181"=>{"string_value"=>""}},
220
- # "4"=>{"182"=>{"integer_value"=>""}, "response_group"=>"4", "question_id"=>"27", "181"=>{"string_value"=>""}}}},
221
-
222
- # 0,1,2,3,4 are the response group numbers
223
- # and anything else in the response group hash is handled normally
2
+ unloadable
3
+ include Surveyor::Models::ResponseSetMethods
4
+ end
data/app/models/survey.rb CHANGED
@@ -1,57 +1,4 @@
1
1
  class Survey < ActiveRecord::Base
2
-
3
- # Associations
4
- has_many :sections, :class_name => "SurveySection", :order => 'display_order'
5
- has_many :sections_with_questions, :include => :questions, :class_name => "SurveySection", :order => 'display_order'
6
- has_many :response_sets
7
-
8
- # Scopes
9
- named_scope :with_sections, {:include => :sections}
10
-
11
- # Validations
12
- validates_presence_of :title
13
- validates_uniqueness_of :access_code
14
-
15
- # Class methods
16
- def self.to_normalized_string(value)
17
- # replace non-alphanumeric with "-". remove repeat "-"s. don't start or end with "-"
18
- value.to_s.downcase.gsub(/[^a-z0-9]/,"-").gsub(/-+/,"-").gsub(/-$|^-/,"")
19
- end
20
-
21
- # Instance methods
22
- def initialize(*args)
23
- super(*args)
24
- default_args
25
- end
26
-
27
- def default_args
28
- self.inactive_at ||= DateTime.now
29
- end
30
-
31
- def title=(value)
32
- self.access_code = Surveyor::Common.to_normalized_string(value)
33
- super
34
- end
35
-
36
- def active?
37
- self.active_as_of?(DateTime.now)
38
- end
39
- def active_as_of?(datetime)
40
- (self.active_at.nil? or self.active_at < datetime) and (self.inactive_at.nil? or self.inactive_at > datetime)
41
- end
42
- def activate!
43
- self.active_at = DateTime.now
44
- end
45
- def deactivate!
46
- self.inactive_at = DateTime.now
47
- end
48
- def active_at=(datetime)
49
- self.inactive_at = nil if !datetime.nil? and !self.inactive_at.nil? and self.inactive_at < datetime
50
- super(datetime)
51
- end
52
- def inactive_at=(datetime)
53
- self.active_at = nil if !datetime.nil? and !self.active_at.nil? and self.active_at > datetime
54
- super(datetime)
55
- end
56
-
2
+ unloadable
3
+ include Surveyor::Models::SurveyMethods
57
4
  end
@@ -1,15 +1,5 @@
1
1
  class SurveySection < ActiveRecord::Base
2
-
3
- # Associations
4
- has_many :questions, :order => "display_order ASC"
5
- belongs_to :survey
6
-
7
- # Scopes
8
- default_scope :order => "display_order ASC"
9
- named_scope :with_includes, { :include => {:questions => [:answers, :question_group, {:dependency => :dependency_conditions}]}}
10
-
11
- # Validations
12
- validates_presence_of :title, :survey, :display_order
13
-
2
+ unloadable
3
+ include Surveyor::Models::SurveySectionMethods
14
4
  end
15
5
 
@@ -1,34 +1,4 @@
1
1
  class Validation < ActiveRecord::Base
2
-
3
- # Associations
4
- belongs_to :answer
5
- has_many :validation_conditions
6
-
7
- # Scopes
8
-
9
- # Validations
10
- validates_presence_of :rule
11
- validates_format_of :rule, :with => /^(?:and|or|\)|\(|[A-Z]|\s)+$/
12
- validates_numericality_of :answer_id
13
-
14
- # Instance Methods
15
- def is_valid?(response_set)
16
- ch = conditions_hash(response_set)
17
- 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
18
- # logger.debug "v: #{self.inspect}"
19
- # logger.debug "rule: #{self.rule.inspect}"
20
- # logger.debug "rexp: #{rgx.inspect}"
21
- # logger.debug "keyp: #{ch.inspect}"
22
- # logger.debug "subd: #{self.rule.gsub(rgx){|m| ch[m.to_sym]}}"
23
- eval(self.rule.gsub(rgx){|m| ch[m.to_sym]})
24
- end
25
-
26
- # A hash of the conditions (keyed by rule_key) and their evaluation (boolean) in the context of response_set
27
- def conditions_hash(response_set)
28
- hash = {}
29
- response = response_set.responses.detect{|r| r.answer_id.to_i == self.answer_id.to_i}
30
- # logger.debug "r: #{response.inspect}"
31
- self.validation_conditions.each{|vc| hash.merge!(vc.to_hash(response))}
32
- return hash
33
- end
2
+ unloadable
3
+ include Surveyor::Models::ValidationMethods
34
4
  end
@@ -1,44 +1,4 @@
1
1
  class ValidationCondition < ActiveRecord::Base
2
-
3
- # Constants
4
- OPERATORS = %w(== != < > <= >= =~)
5
-
6
- # Associations
7
- belongs_to :validation
8
-
9
- # Scopes
10
-
11
- # Validations
12
- validates_numericality_of :validation_id #, :question_id, :answer_id
13
- validates_presence_of :operator, :rule_key
14
- validates_inclusion_of :operator, :in => OPERATORS
15
- validates_uniqueness_of :rule_key, :scope => :validation_id
16
-
17
- acts_as_response # includes "as" instance method
18
-
19
- # Class methods
20
- def self.operators
21
- OPERATORS
22
- end
23
-
24
- # Instance Methods
25
- def to_hash(response)
26
- {rule_key.to_sym => (response.nil? ? false : self.is_valid?(response))}
27
- end
28
-
29
- def is_valid?(response)
30
- klass = response.answer.response_class
31
- compare_to = Response.find_by_question_id_and_answer_id(self.question_id, self.answer_id) || self
32
- case self.operator
33
- when "==", "<", ">", "<=", ">="
34
- response.as(klass).send(self.operator, compare_to.as(klass))
35
- when "!="
36
- !(response.as(klass) == compare_to.as(klass))
37
- when "=~"
38
- return false if compare_to != self
39
- !(response.as(klass).to_s =~ Regexp.new(self.regexp || "")).nil?
40
- else
41
- false
42
- end
43
- end
2
+ unloadable
3
+ include Surveyor::Models::ValidationConditionMethods
44
4
  end
@@ -21,6 +21,7 @@ Surveyor's models can all be customized:
21
21
  For example, create app/models/survey.rb with the following contents:
22
22
 
23
23
  class Survey < ActiveRecord::Base
24
+ include Surveyor::Models::SurveyMethods
24
25
  def title
25
26
  "Custom #{super}"
26
27
  end