active_record_survey 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/lib/active_record_survey/instance_node.rb +8 -2
- data/lib/active_record_survey/node/answer/boolean.rb +8 -0
- data/lib/active_record_survey/node/answer.rb +8 -0
- data/lib/active_record_survey/node.rb +38 -2
- data/lib/active_record_survey/node_map.rb +11 -0
- data/lib/active_record_survey/node_validation/maximum_answer.rb +22 -3
- data/lib/active_record_survey/node_validation/minimum_answer.rb +22 -3
- data/lib/active_record_survey/node_validation.rb +5 -1
- data/lib/active_record_survey/version.rb +1 -1
- data/spec/active_record_survey/node/answer/boolean_spec.rb +113 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc0440e0c23ed5d966185f62fe83619c7d50fa01
|
4
|
+
data.tar.gz: b0e93efb11a6994878ce6c3d5d948043525125a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81b0ea583a5d440eddb185547a584dcf471d8f1618a4f8a0e80406882d7a866c5391851e9eb962f28a90f6308bd3c2aba0c30891b700e28a9737acf19fc04f1e
|
7
|
+
data.tar.gz: 64b747965c5c7bcefe1753f4518f5f4dcbf5279339c459ed1c60a64d854854f77f88ba12410cbd76bb8fd02da05d671fdf1098405405c82384fe19d750eefad8
|
@@ -15,12 +15,18 @@ module ActiveRecordSurvey
|
|
15
15
|
# Two instance_nodes on the same node for this instance
|
16
16
|
if self.instance.instance_nodes.select { |i|
|
17
17
|
# Two votes share a parent (this means a question has two answers for this instance)
|
18
|
-
(i.node.node_maps.collect { |j|
|
18
|
+
(i.node.node_maps.collect { |j|
|
19
|
+
j.parent
|
20
|
+
} & self.node.node_maps.collect { |j|
|
21
|
+
j.parent
|
22
|
+
}).length > 0
|
19
23
|
}.length > 1
|
20
24
|
instance_node.errors[:base] << "DUPLICATE_PATH"
|
21
25
|
end
|
22
26
|
|
23
|
-
|
27
|
+
if !self.node.validate_instance_node(self)
|
28
|
+
instance_node.errors[:base] << "INVALID"
|
29
|
+
end
|
24
30
|
end
|
25
31
|
end
|
26
32
|
end
|
@@ -7,5 +7,13 @@ module ActiveRecordSurvey
|
|
7
7
|
super &&
|
8
8
|
!instance_node.value.to_s.match(/^[0|1]$/).nil?
|
9
9
|
end
|
10
|
+
|
11
|
+
# Boolean answers are considered answered if they have a value of "1"
|
12
|
+
def is_answered_for_instance?(instance)
|
13
|
+
if instance_node = self.instance_node_for_instance(instance)
|
14
|
+
# Instance node is answered "1"
|
15
|
+
(instance_node.value.to_i === 1)
|
16
|
+
end
|
17
|
+
end
|
10
18
|
end
|
11
19
|
end
|
@@ -1,4 +1,12 @@
|
|
1
1
|
module ActiveRecordSurvey
|
2
2
|
class Node::Answer < Node
|
3
|
+
# Answer nodes are valid if their questions are valid!
|
4
|
+
# Validate this node against an instance
|
5
|
+
def validate_node(instance)
|
6
|
+
# Ensure each parent node to this node (the goal here is to hit a question node) is valid
|
7
|
+
!self.node_maps.collect { |node_map|
|
8
|
+
node_map.parent.node.validate_node(instance)
|
9
|
+
}.include?(false)
|
10
|
+
end
|
3
11
|
end
|
4
12
|
end
|
@@ -2,12 +2,48 @@ module ActiveRecordSurvey
|
|
2
2
|
class Node < ::ActiveRecord::Base
|
3
3
|
self.table_name = "active_record_survey_nodes"
|
4
4
|
has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_node_id
|
5
|
-
has_many :node_validations, :class_name => "ActiveRecordSurvey::NodeValidation", :foreign_key => :active_record_survey_node_id
|
5
|
+
has_many :node_validations, :class_name => "ActiveRecordSurvey::NodeValidation", :foreign_key => :active_record_survey_node_id, autosave: true
|
6
|
+
has_many :instance_nodes, :class_name => "ActiveRecordSurvey::InstanceNode", :foreign_key => :active_record_survey_node_id
|
6
7
|
|
7
|
-
#
|
8
|
+
# The instance_node recorded for the passed instance for this node
|
9
|
+
def instance_node_for_instance(instance)
|
10
|
+
instance.instance_nodes.select { |instance_node|
|
11
|
+
(instance_node.node === self)
|
12
|
+
}.first
|
13
|
+
end
|
14
|
+
|
15
|
+
# Whether this node has an answer recorded the instance
|
16
|
+
def has_instance_node_for_instance?(instance)
|
17
|
+
!self.instance_node_for_instance(instance).nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Whether considered answered for instance
|
21
|
+
#
|
22
|
+
# Is answered is a little different than has_answer
|
23
|
+
# Is answered is answer type specific, as what constitutes "answered" changes depending on
|
24
|
+
# the question type asked (e.g. boolean is answered if "1")
|
25
|
+
#
|
26
|
+
# Each specific answer type should override this method if they have special criteria for answered
|
27
|
+
#
|
28
|
+
# default - if instance node exists, answered
|
29
|
+
def is_answered_for_instance?(instance)
|
30
|
+
self.has_instance_node_for_instance?(instance)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Run all validations applied to this node
|
8
34
|
def validate_instance_node(instance_node)
|
35
|
+
# UGH - so bsaically this validation doesn't know about the non-saved validation..
|
36
|
+
#puts "Valdating #{self.id} - #{self.text} - total validations are - #{self.node_validations(true).length}"
|
9
37
|
!self.node_validations.collect { |node_validation|
|
10
38
|
node_validation.validate_instance_node(instance_node, self)
|
39
|
+
}.include?(false) &&
|
40
|
+
!self.node_maps.collect { |node_map|
|
41
|
+
if node_map.parent
|
42
|
+
node_map.parent.node.validate_instance_node(instance_node)
|
43
|
+
# Hit top node
|
44
|
+
else
|
45
|
+
true
|
46
|
+
end
|
11
47
|
}.include?(false)
|
12
48
|
end
|
13
49
|
|
@@ -22,5 +22,16 @@ module ActiveRecordSurvey
|
|
22
22
|
}
|
23
23
|
}
|
24
24
|
end
|
25
|
+
|
26
|
+
# Gets all the child nodes until one is not an ancestor of klass
|
27
|
+
def children_until_node_not_ancestor_of(klass)
|
28
|
+
if !self.node.class.ancestors.include?(klass)
|
29
|
+
return []
|
30
|
+
end
|
31
|
+
|
32
|
+
[self] + self.children.collect { |i|
|
33
|
+
i.children_until_node_not_ancestor_of(klass)
|
34
|
+
}
|
35
|
+
end
|
25
36
|
end
|
26
37
|
end
|
@@ -2,9 +2,28 @@ module ActiveRecordSurvey
|
|
2
2
|
# Ensure the a maximum number of answers are made
|
3
3
|
class NodeValidation::MaximumAnswer < NodeValidation
|
4
4
|
# Validate the instance_node to ensure a maximum number of answers are made
|
5
|
-
def validate_instance_node(instance_node,
|
6
|
-
|
7
|
-
|
5
|
+
def validate_instance_node(instance_node, question_node = nil)
|
6
|
+
#puts "-------------------------------------------"
|
7
|
+
#puts "runnin min answer validation"
|
8
|
+
|
9
|
+
# Only makes sense for questions to have minimum answers
|
10
|
+
if !question_node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
instance = instance_node.instance
|
15
|
+
|
16
|
+
# Go through the node_map of this node
|
17
|
+
total_answered = question_node.node_maps.collect { |question_node_map|
|
18
|
+
# Get all children until a childs node isn't an answer
|
19
|
+
question_node_map.children.collect { |i|
|
20
|
+
i.children_until_node_not_ancestor_of(::ActiveRecordSurvey::Node::Answer)
|
21
|
+
}.flatten.collect { |i|
|
22
|
+
i.node.is_answered_for_instance?(instance)
|
23
|
+
}
|
24
|
+
}.flatten.select { |i| i }.count
|
25
|
+
|
26
|
+
total_answered <= self.value.to_i
|
8
27
|
end
|
9
28
|
end
|
10
29
|
end
|
@@ -2,9 +2,28 @@ module ActiveRecordSurvey
|
|
2
2
|
# Ensure the a minimum number of answers are made
|
3
3
|
class NodeValidation::MinimumAnswer < NodeValidation
|
4
4
|
# Validate the instance_node to ensure a minimum number of answers are made
|
5
|
-
def validate_instance_node(instance_node,
|
6
|
-
|
7
|
-
|
5
|
+
def validate_instance_node(instance_node, question_node = nil)
|
6
|
+
#puts "-------------------------------------------"
|
7
|
+
#puts "runnin min answer validation"
|
8
|
+
|
9
|
+
# Only makes sense for questions to have minimum answers
|
10
|
+
if !question_node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
instance = instance_node.instance
|
15
|
+
|
16
|
+
# Go through the node_map of this node
|
17
|
+
total_answered = question_node.node_maps.collect { |question_node_map|
|
18
|
+
# Get all children until a childs node isn't an answer
|
19
|
+
question_node_map.children.collect { |i|
|
20
|
+
i.children_until_node_not_ancestor_of(::ActiveRecordSurvey::Node::Answer)
|
21
|
+
}.flatten.collect { |i|
|
22
|
+
i.node.is_answered_for_instance?(instance)
|
23
|
+
}
|
24
|
+
}.flatten.select { |i| i }.count
|
25
|
+
|
26
|
+
total_answered >= self.value.to_i
|
8
27
|
end
|
9
28
|
end
|
10
29
|
end
|
@@ -5,8 +5,12 @@ module ActiveRecordSurvey
|
|
5
5
|
belongs_to :node, :class_name => "ActiveRecordSurvey::Node", :foreign_key => :active_record_survey_node_id
|
6
6
|
|
7
7
|
# By default everything is valid! WOOO!
|
8
|
-
def validate_instance_node(instance_node
|
8
|
+
def validate_instance_node(instance_node)
|
9
9
|
true
|
10
10
|
end
|
11
|
+
|
12
|
+
def validate_node(instance_node, node)
|
13
|
+
#code
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
@@ -1,18 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe ActiveRecordSurvey::Node::Answer::Boolean do
|
3
|
+
describe ActiveRecordSurvey::Node::Answer::Boolean, :focus => true do
|
4
4
|
describe 'a boolean survey is' do
|
5
5
|
before(:all) do
|
6
6
|
@survey = ActiveRecordSurvey::Survey.new
|
7
7
|
|
8
|
-
@q1 = ActiveRecordSurvey::Node::Question.new(:text => "Please select
|
8
|
+
@q1 = ActiveRecordSurvey::Node::Question.new(:text => "Please select two")
|
9
9
|
@q1_a1 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Dog")
|
10
10
|
@q1_a2 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Cat")
|
11
11
|
@q1_a3 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Mouse")
|
12
|
+
@q1_a4 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Tiger")
|
13
|
+
@q1_a5 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Bear")
|
12
14
|
|
13
15
|
nodes = @survey.build_question(@q1, [@q1_a1])
|
14
16
|
nodes = @survey.build_question(@q1_a2, [], nodes[1])
|
15
17
|
nodes = @survey.build_question(@q1_a3, [], nodes[0])
|
18
|
+
nodes = @survey.build_question(@q1_a4, [], nodes[0])
|
19
|
+
nodes = @survey.build_question(@q1_a5, [], nodes[0])
|
20
|
+
|
21
|
+
@q1.node_validations << ActiveRecordSurvey::NodeValidation::MinimumAnswer.new(
|
22
|
+
:node => @q1,
|
23
|
+
:value => 1 # min 1 of the 3 answers must be "answered"
|
24
|
+
)
|
25
|
+
@q1.node_validations << ActiveRecordSurvey::NodeValidation::MaximumAnswer.new(
|
26
|
+
:node => @q1,
|
27
|
+
:value => 3 # max 2 of the 3 answers must be "answered"
|
28
|
+
)
|
16
29
|
|
17
30
|
@survey.save
|
18
31
|
end
|
@@ -37,6 +50,11 @@ describe ActiveRecordSurvey::Node::Answer::Boolean do
|
|
37
50
|
:node => @q1_a1,
|
38
51
|
:value => 0
|
39
52
|
)
|
53
|
+
instance.instance_nodes.build(
|
54
|
+
:instance => instance,
|
55
|
+
:node => @q1_a2,
|
56
|
+
:value => 1
|
57
|
+
)
|
40
58
|
instance.save
|
41
59
|
|
42
60
|
expect(instance.valid?).to be(true)
|
@@ -94,40 +112,122 @@ describe ActiveRecordSurvey::Node::Answer::Boolean do
|
|
94
112
|
describe ActiveRecordSurvey::NodeValidation::MinimumAnswer do
|
95
113
|
describe 'valid when' do
|
96
114
|
it 'has a value greater than the minimum' do
|
97
|
-
@q1.node_validations << ActiveRecordSurvey::NodeValidation::MinimumAnswer.new(
|
98
|
-
:node => @q1,
|
99
|
-
:value => 2 # 2 of the 3 answers must be "answered"
|
100
|
-
)
|
101
115
|
instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
|
102
116
|
instance.instance_nodes.build(
|
103
117
|
:instance => instance,
|
104
118
|
:node => @q1_a1,
|
105
|
-
:value =>
|
119
|
+
:value => 1,
|
120
|
+
)
|
121
|
+
instance.instance_nodes.build(
|
122
|
+
:instance => instance,
|
123
|
+
:node => @q1_a2,
|
124
|
+
:value => 0,
|
125
|
+
)
|
126
|
+
instance.instance_nodes.build(
|
127
|
+
:instance => instance,
|
128
|
+
:node => @q1_a3,
|
129
|
+
:value => 1,
|
106
130
|
)
|
107
131
|
instance.save
|
108
132
|
|
109
|
-
|
133
|
+
expect(instance.valid?).to be(true)
|
110
134
|
end
|
111
135
|
end
|
112
|
-
|
136
|
+
|
113
137
|
describe 'invalid when' do
|
114
138
|
it 'has a value less than the minimum' do
|
115
|
-
|
139
|
+
instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
|
140
|
+
instance.instance_nodes.build(
|
141
|
+
:instance => instance,
|
116
142
|
:node => @q1_a1,
|
117
|
-
:value =>
|
143
|
+
:value => 0,
|
118
144
|
)
|
145
|
+
instance.instance_nodes.build(
|
146
|
+
:instance => instance,
|
147
|
+
:node => @q1_a2,
|
148
|
+
:value => 0,
|
149
|
+
)
|
150
|
+
instance.instance_nodes.build(
|
151
|
+
:instance => instance,
|
152
|
+
:node => @q1_a3,
|
153
|
+
:value => 0,
|
154
|
+
)
|
155
|
+
instance.save
|
156
|
+
|
157
|
+
expect(instance.valid?).to be(false)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
describe ActiveRecordSurvey::NodeValidation::MaximumAnswer do
|
164
|
+
describe 'valid when' do
|
165
|
+
it 'has a value less than the maximum' do
|
119
166
|
instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
|
120
167
|
instance.instance_nodes.build(
|
121
168
|
:instance => instance,
|
122
169
|
:node => @q1_a1,
|
123
|
-
:value =>
|
170
|
+
:value => 1,
|
171
|
+
)
|
172
|
+
instance.instance_nodes.build(
|
173
|
+
:instance => instance,
|
174
|
+
:node => @q1_a2,
|
175
|
+
:value => 0,
|
176
|
+
)
|
177
|
+
instance.instance_nodes.build(
|
178
|
+
:instance => instance,
|
179
|
+
:node => @q1_a3,
|
180
|
+
:value => 1,
|
181
|
+
)
|
182
|
+
instance.instance_nodes.build(
|
183
|
+
:instance => instance,
|
184
|
+
:node => @q1_a4,
|
185
|
+
:value => 1,
|
186
|
+
)
|
187
|
+
instance.instance_nodes.build(
|
188
|
+
:instance => instance,
|
189
|
+
:node => @q1_a5,
|
190
|
+
:value => 0,
|
124
191
|
)
|
125
192
|
instance.save
|
193
|
+
|
194
|
+
expect(instance.valid?).to be(true)
|
195
|
+
end
|
196
|
+
end
|
126
197
|
|
198
|
+
describe 'invalid when' do
|
199
|
+
it 'has a value greater than the maximum' do
|
200
|
+
instance = ActiveRecordSurvey::Instance.new(:survey => @survey)
|
201
|
+
instance.instance_nodes.build(
|
202
|
+
:instance => instance,
|
203
|
+
:node => @q1_a1,
|
204
|
+
:value => 1,
|
205
|
+
)
|
206
|
+
instance.instance_nodes.build(
|
207
|
+
:instance => instance,
|
208
|
+
:node => @q1_a2,
|
209
|
+
:value => 1,
|
210
|
+
)
|
211
|
+
instance.instance_nodes.build(
|
212
|
+
:instance => instance,
|
213
|
+
:node => @q1_a3,
|
214
|
+
:value => 1,
|
215
|
+
)
|
216
|
+
instance.instance_nodes.build(
|
217
|
+
:instance => instance,
|
218
|
+
:node => @q1_a4,
|
219
|
+
:value => 1,
|
220
|
+
)
|
221
|
+
instance.instance_nodes.build(
|
222
|
+
:instance => instance,
|
223
|
+
:node => @q1_a5,
|
224
|
+
:value => 1,
|
225
|
+
)
|
226
|
+
instance.save
|
227
|
+
|
127
228
|
expect(instance.valid?).to be(false)
|
128
229
|
end
|
129
230
|
end
|
130
|
-
=end
|
131
231
|
end
|
132
232
|
end
|
133
233
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record_survey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Butch Marshall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-09-
|
11
|
+
date: 2015-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|