active_record_survey 0.1.35 → 0.1.36

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e024c72de4b41b4e3937d465726d0110cadbcf83
4
- data.tar.gz: 41352a433cb8b8a6b8f0d8779c7ce8ced446cc4a
3
+ metadata.gz: be07fe6de1d3387ccd71f4c56ea9e33535baf95d
4
+ data.tar.gz: 8a24d9f1c4459a1f2131b28782dbc035ccbace2a
5
5
  SHA512:
6
- metadata.gz: 6f70aea6ac4dd79b3ff9e6b6b97096ca52ea839d6241b39aaef8740ad5ec91d1f9ce18fa7acd5fe5ce7ff5db1657b10586b2effa4fa1a1c7a0f8fca3d89b65a5
7
- data.tar.gz: 77f86f2a2b516981e85cff63cd97bd2fb570cf7014c241894774c6c5a7ff0330fcbcbf226d93b1b02215e40b4b54ffc1c9f26450134c55b46843b93addcfb789
6
+ metadata.gz: 7a533e0173ba238d96430fe78348414cb76249abfd568cc4c6f920d745d920ddc3ea3b12462bb2d4577def1b4d8efefed0ccd2b702c49f9e4c3a822a8f4fa779
7
+ data.tar.gz: 13cd48140d0a0050e487e5a615afa3040b19763268a926db3d93e7758c726f261866f14aa21b3970893508aefadb429e9c47460476c5400fa6c6107ea54aa56d
data/README.md CHANGED
@@ -12,6 +12,10 @@ The goal is to give a simple interface for creating surveys and validating the a
12
12
  Release Notes
13
13
  ============
14
14
 
15
+ **0.1.36**
16
+ - `ActiveRecordSurvey::Node::Answer#build_link` and `ActiveRecordSurvey::Node::Answer#remove_link` moved to `ActiveRecordSurvey::Node` so that questions can directly follow one another without answers
17
+ - Implemented `ActiveRecordSurvey::Node::Question#next_questions` to return all questions that follow either directly or through answers
18
+
15
19
  **0.1.33**
16
20
  - `ActiveRecordSurvey::Node::Answer#sibling_index` method for setting position as well
17
21
 
@@ -3,7 +3,6 @@ module ActiveRecordSurvey
3
3
  module Chained
4
4
  module ClassMethods
5
5
  def self.extended(base)
6
- base.before_destroy :before_destroy_rebuild_node_map, prepend: true # prepend is important! otherwise dependent: :destroy on node<->node_map relation is executed first and no records!
7
6
  end
8
7
  end
9
8
 
@@ -123,21 +122,6 @@ module ActiveRecordSurvey
123
122
  end
124
123
  }
125
124
  end
126
-
127
- private
128
- # Before a node is destroyed, will re-build the node_map links from parent to child if they exist
129
- def before_destroy_rebuild_node_map
130
-
131
- # All the node_maps from this node
132
- self.survey.node_maps.select { |i|
133
- i.node == self
134
- }.each { |node_map|
135
- # Remap all of this nodes children to the parent
136
- node_map.children.each { |child|
137
- node_map.parent.children << child
138
- }
139
- }
140
- end
141
125
  end
142
126
  end
143
127
  end
@@ -50,89 +50,12 @@ module ActiveRecordSurvey
50
50
  return nil
51
51
  end
52
52
 
53
- # Removes the link
54
- def remove_link
55
- # not linked to a question - nothing to remove!
56
- return true if (question = self.next_question).nil?
57
-
58
- count = 0
59
- to_remove = []
60
- self.survey.node_maps.each { |node_map|
61
- if node_map.node == question
62
- if count > 0
63
- to_remove.concat(node_map.self_and_descendants)
64
- else
65
- node_map.parent = nil
66
- end
67
- count = count + 1
68
- end
69
-
70
- if node_map.node == self
71
- node_map.children = []
72
- end
73
- }
74
- self.survey.node_maps.each { |node_map|
75
- if to_remove.include?(node_map)
76
- node_map.parent = nil
77
- node_map.mark_for_destruction
78
- end
79
- }
80
- end
81
-
82
- # Build a link from this node to another node
83
- # Building a link actually needs to throw off a whole new clone of all children nodes
84
53
  def build_link(to_node)
85
- # build_link only accepts a to_node that inherits from Question
86
- if !to_node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
87
- raise ArgumentError.new "to_node must inherit from ::ActiveRecordSurvey::Node::Question"
88
- end
89
-
90
54
  if self.question.nil?
91
55
  raise ArgumentError.new "A question is required before calling #build_link"
92
56
  end
93
57
 
94
- if self.survey.nil?
95
- raise ArgumentError.new "A survey is required before calling #build_link"
96
- end
97
-
98
- from_node_maps = self.survey.node_maps.select { |i| i.node == self && !i.marked_for_destruction? }
99
-
100
- # Answer has already got a question - throw error
101
- if from_node_maps.select { |i|
102
- i.children.length === 0
103
- }.length === 0
104
- raise RuntimeError.new "This answer has already been linked"
105
- end
106
-
107
- # Because we need something to clone - filter this further below
108
- to_node_maps = self.survey.node_maps.select { |i| i.node == to_node && !i.marked_for_destruction? }
109
-
110
- if to_node_maps.first.nil?
111
- to_node_maps << self.survey.node_maps.build(:survey => self.survey, :node => to_node)
112
- end
113
-
114
- # Ensure we can through each possible path of getting to this answer
115
- to_node_map = to_node_maps.first
116
- to_node_map.survey = self.survey # required due to voodoo - we want to use the same survey with the same object_id
117
-
118
- # We only want node maps that aren't linked somewhere
119
- to_node_maps = to_node_maps.select { |i| i.parent.nil? }
120
- while to_node_maps.length < from_node_maps.length do
121
- to_node_maps.push(to_node_map.recursive_clone)
122
- end
123
-
124
- # Link unused node_maps to the new parents
125
- from_node_maps.each_with_index { |from_node_map, index|
126
- from_node_map.children << to_node_maps[index]
127
- }
128
-
129
- # Ensure no infinite loops were created
130
- from_node_maps.each { |node_map|
131
- # There is a path from Q -> A that is a loop
132
- if node_map.has_infinite_loop?
133
- raise RuntimeError.new "Infinite loop detected"
134
- end
135
- }
58
+ super(to_node)
136
59
  end
137
60
 
138
61
  # Gets index in sibling relationship
@@ -25,5 +25,26 @@ module ActiveRecordSurvey
25
25
  # Answers actually define how they're built off the parent node
26
26
  answer_node.send(:build_answer, self)
27
27
  end
28
+
29
+ # Returns the questions that follows this question (either directly or via its answers)
30
+ def next_questions
31
+ list = []
32
+
33
+ if question_node_map = self.survey.node_maps.select { |i|
34
+ i.node == self && !i.marked_for_destruction?
35
+ }.first
36
+ question_node_map.children.each { |child|
37
+ if !child.node.nil? && !child.marked_for_destruction?
38
+ if child.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
39
+ list << child.node
40
+ elsif child.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Answer)
41
+ list << child.node.next_question
42
+ end
43
+ end
44
+ }
45
+ end
46
+
47
+ list.compact.uniq
48
+ end
28
49
  end
29
50
  end
@@ -6,6 +6,8 @@ module ActiveRecordSurvey
6
6
  has_many :node_validations, :class_name => "ActiveRecordSurvey::NodeValidation", :foreign_key => :active_record_survey_node_id, autosave: true, dependent: :destroy
7
7
  has_many :instance_nodes, :class_name => "ActiveRecordSurvey::InstanceNode", :foreign_key => :active_record_survey_node_id
8
8
 
9
+ before_destroy :before_destroy_rebuild_node_map, prepend: true # prepend is important! otherwise dependent: :destroy on node<->node_map relation is executed first and no records!
10
+
9
11
  # All the answer nodes that follow from this node
10
12
  def answers
11
13
  self.survey.node_maps.select { |i|
@@ -113,5 +115,102 @@ module ActiveRecordSurvey
113
115
  # If recursion reports back to have at least one valid path to root
114
116
  paths.include?(true)
115
117
  end
118
+
119
+ # Removes the node_map link
120
+ def remove_link
121
+ # not linked to a question - nothing to remove!
122
+ return true if (question = self.next_question).nil?
123
+
124
+ count = 0
125
+ to_remove = []
126
+ self.survey.node_maps.each { |node_map|
127
+ if node_map.node == question
128
+ if count > 0
129
+ to_remove.concat(node_map.self_and_descendants)
130
+ else
131
+ node_map.parent = nil
132
+ end
133
+ count = count + 1
134
+ end
135
+
136
+ if node_map.node == self
137
+ node_map.children = []
138
+ end
139
+ }
140
+ self.survey.node_maps.each { |node_map|
141
+ if to_remove.include?(node_map)
142
+ node_map.parent = nil
143
+ node_map.mark_for_destruction
144
+ end
145
+ }
146
+ end
147
+
148
+ # Build a link from this node to another node
149
+ # Building a link actually needs to throw off a whole new clone of all children nodes
150
+ def build_link(to_node)
151
+ # build_link only accepts a to_node that inherits from Question
152
+ if !to_node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
153
+ raise ArgumentError.new "to_node must inherit from ::ActiveRecordSurvey::Node::Question"
154
+ end
155
+
156
+ if self.survey.nil?
157
+ raise ArgumentError.new "A survey is required before calling #build_link"
158
+ end
159
+
160
+ from_node_maps = self.survey.node_maps.select { |i| i.node == self && !i.marked_for_destruction? }
161
+
162
+ # Answer has already got a question - throw error
163
+ if from_node_maps.select { |i|
164
+ i.children.length === 0
165
+ }.length === 0
166
+ raise RuntimeError.new "This node has already been linked"
167
+ end
168
+
169
+ # Because we need something to clone - filter this further below
170
+ to_node_maps = self.survey.node_maps.select { |i| i.node == to_node && !i.marked_for_destruction? }
171
+
172
+ if to_node_maps.first.nil?
173
+ to_node_maps << self.survey.node_maps.build(:survey => self.survey, :node => to_node)
174
+ end
175
+
176
+ # Ensure we can through each possible path of getting to this answer
177
+ to_node_map = to_node_maps.first
178
+ to_node_map.survey = self.survey # required due to voodoo - we want to use the same survey with the same object_id
179
+
180
+ # We only want node maps that aren't linked somewhere
181
+ to_node_maps = to_node_maps.select { |i| i.parent.nil? }
182
+ while to_node_maps.length < from_node_maps.length do
183
+ to_node_maps.push(to_node_map.recursive_clone)
184
+ end
185
+
186
+ # Link unused node_maps to the new parents
187
+ from_node_maps.each_with_index { |from_node_map, index|
188
+ from_node_map.children << to_node_maps[index]
189
+ }
190
+
191
+ # Ensure no infinite loops were created
192
+ from_node_maps.each { |node_map|
193
+ # There is a path from Q -> A that is a loop
194
+ if node_map.has_infinite_loop?
195
+ raise RuntimeError.new "Infinite loop detected"
196
+ end
197
+ }
198
+ end
199
+
200
+ private
201
+ # Before a node is destroyed, will re-build the node_map links from parent to child if they exist
202
+ def before_destroy_rebuild_node_map
203
+ # All the node_maps from this node
204
+ self.survey.node_maps.select { |i|
205
+ i.node == self
206
+ }.each { |node_map|
207
+ # Remap all of this nodes children to the parent
208
+ node_map.children.each { |child|
209
+ node_map.parent.children << child
210
+ }
211
+ }
212
+
213
+ true
214
+ end
116
215
  end
117
216
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordSurvey
2
- VERSION = "0.1.35"
2
+ VERSION = "0.1.36"
3
3
  end
@@ -1,6 +1,88 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ActiveRecordSurvey::Node::Question, :question_spec => true do
4
+ describe "#next_questions" do
5
+ it "should return an array of all questions following a question, whether they have answers or not" do
6
+ @survey = ActiveRecordSurvey::Survey.new()
7
+
8
+ @q1 = ActiveRecordSurvey::Node::Question.new(:text => "Question #1", :survey => @survey)
9
+ @q1_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #1")
10
+ @q1_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #2")
11
+ @q1_a3 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #3")
12
+ @q1_a4 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #4")
13
+ @q1.build_answer(@q1_a1)
14
+ @q1.build_answer(@q1_a2)
15
+ @q1.build_answer(@q1_a3)
16
+ @q1.build_answer(@q1_a4)
17
+
18
+ @q2 = ActiveRecordSurvey::Node::Question.new(:text => "Question #2", :survey => @survey)
19
+ @q2_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 Answer #1")
20
+ @q2_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 Answer #2")
21
+ @q2.build_answer(@q2_a1)
22
+ @q2.build_answer(@q2_a2)
23
+
24
+ @q3 = ActiveRecordSurvey::Node::Question.new(:text => "Question #3", :survey => @survey)
25
+ @q3_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 Answer #1")
26
+ @q3_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 Answer #2")
27
+ @q3.build_answer(@q3_a1)
28
+ @q3.build_answer(@q3_a2)
29
+
30
+ @q4 = ActiveRecordSurvey::Node::Question.new(:text => "Question #4", :survey => @survey)
31
+ @q4_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 Answer #1")
32
+ @q4_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 Answer #2")
33
+ @q4.build_answer(@q4_a1)
34
+ @q4.build_answer(@q4_a2)
35
+
36
+ @q5 = ActiveRecordSurvey::Node::Question.new(:text => "Question #5", :survey => @survey)
37
+
38
+ @q6 = ActiveRecordSurvey::Node::Question.new(:text => "Question #6", :survey => @survey)
39
+ @q6_a1 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Q6 Answer #1")
40
+ @q6_a2 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "Q6 Answer #2")
41
+ @q6.build_answer(@q6_a1)
42
+ @q6.build_answer(@q6_a2)
43
+
44
+ @q7 = ActiveRecordSurvey::Node::Question.new(:text => "Question #7", :survey => @survey)
45
+
46
+ # Link up Q1
47
+ @q1_a1.build_link(@q2)
48
+ @q1_a2.build_link(@q3)
49
+ @q1_a3.build_link(@q4)
50
+
51
+ # Link up Q2
52
+ @q2_a1.build_link(@q4)
53
+ @q2_a2.build_link(@q3)
54
+
55
+ # Link up Q3
56
+ @q3_a1.build_link(@q4)
57
+ @q3_a2.build_link(@q4)
58
+
59
+ # Link up Q1A4 -> Q5
60
+ @q1_a4.build_link(@q5)
61
+
62
+ # Link up Q5 -> Q6
63
+ @q5.build_link(@q6)
64
+
65
+ # Link up Q6 -> Q7
66
+ @q6_a2.build_link(@q7)
67
+
68
+ @survey.save
69
+
70
+ q1_next_questions = @q1.next_questions
71
+ q2_next_questions = @q2.next_questions
72
+ q3_next_questions = @q3.next_questions
73
+ q4_next_questions = @q4.next_questions
74
+ q5_next_questions = @q5.next_questions
75
+ q6_next_questions = @q6.next_questions
76
+
77
+ expect(q1_next_questions.length).to eq(4)
78
+ expect(q2_next_questions.length).to eq(2)
79
+ expect(q3_next_questions.length).to eq(1)
80
+ expect(q4_next_questions.length).to eq(0)
81
+ expect(q5_next_questions.length).to eq(1)
82
+ expect(q6_next_questions.length).to eq(1)
83
+ end
84
+ end
85
+
4
86
  describe "#build_answer" do
5
87
  before(:each) do
6
88
  @survey = ActiveRecordSurvey::Survey.new()
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.35
4
+ version: 0.1.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Butch Marshall
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-26 00:00:00.000000000 Z
11
+ date: 2016-03-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord