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
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