active_record_survey 0.1.25 → 0.1.26

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2e31b5194f981037636d99848bcf20cca47ff08d
4
- data.tar.gz: 826afd06292c6edb0c7c66c125d09f719960b656
3
+ metadata.gz: d9764b3647e3386e4d60b9753940515b78881b88
4
+ data.tar.gz: 899bf6944ac349d04d2aca7b20254837a2a93bd2
5
5
  SHA512:
6
- metadata.gz: d549e57755d6ccbfdd39d65a5250076de67bf434412f8ecbdce4009bfceb275ef935acd4f49bec8025269fc35127f08e9810a4b574889bd7c3cab7ccd2154f0b
7
- data.tar.gz: bda3002f375e5dccff9be201873e2ca52fa3a482271ea4f730035f20c3e46ed01e8bbffc7706454f72179589e8e8b7d34feaf8717f5451823916d8755f9ec783
6
+ metadata.gz: 765649c5e0aa37a8b02fac5ef171852d45f4a5145ea414f1486a4fa73f3b95834bee2f68192145681bd3975cba6e1b580eb09d959e139a9c59ea95f2b1d46402
7
+ data.tar.gz: b23afb8a510de738c55fd823fdc03d498471b4e4c65e4b3eca5a9ff7a919a5359e066b8e0ea782b4adeddbb28083f33c8310d034f24e6d73c4dd9377752e9a26
data/README.md CHANGED
@@ -12,6 +12,11 @@ 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.26**
16
+ - Major refactor of answer#build_link and answer#remove_link
17
+ - `ActiveRecordSurvey::Node` now has a direct reference to its survey. Don't forget to run the install task Update_0_1_26_ActiveRecordSurvey
18
+ - survey#build_question removed, no longer needed, just use survey.questions.build
19
+
15
20
  **0.1.24**
16
21
  - Refactored class `ActiveRecordSurvey::Node::Answer::Chain` to module `ActiveRecordSurvey::Node::Answer::Chained` - this functionality makes way more sense implemented as a module.
17
22
 
@@ -72,38 +77,34 @@ The usage below with `:text => ""` will not actually work unless you implement `
72
77
  ### Build a basic survey
73
78
  ```ruby
74
79
 
75
- # New method for building surveys
80
+ # Building surveys
76
81
  @survey = ActiveRecordSurvey::Survey.new()
77
82
 
78
- @q1 = ActiveRecordSurvey::Node::Question.new(:text => "Question #1")
79
- @survey.build_question(@q1)
83
+ @q1 = @survey.questions.build(:type => "ActiveRecordSurvey::Node::Question", :text => "Question #1", :survey => @survey)
80
84
  @q1_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #1")
81
85
  @q1_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #2")
82
86
  @q1_a3 = ActiveRecordSurvey::Node::Answer.new(:text => "Q1 Answer #3")
83
- @q1.build_answer(@q1_a1, @survey)
84
- @q1.build_answer(@q1_a2, @survey)
85
- @q1.build_answer(@q1_a3, @survey)
87
+ @q1.build_answer(@q1_a1)
88
+ @q1.build_answer(@q1_a2)
89
+ @q1.build_answer(@q1_a3)
86
90
 
87
- @q2 = ActiveRecordSurvey::Node::Question.new(:text => "Question #2")
88
- @survey.build_question(@q2)
91
+ @q2 = @survey.questions.build(:type => "ActiveRecordSurvey::Node::Question", :text => "Question #2", :survey => @survey)
89
92
  @q2_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 Answer #1")
90
93
  @q2_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q2 Answer #2")
91
- @q2.build_answer(@q2_a1, @survey)
92
- @q2.build_answer(@q2_a2, @survey)
94
+ @q2.build_answer(@q2_a1)
95
+ @q2.build_answer(@q2_a2)
93
96
 
94
- @q3 = ActiveRecordSurvey::Node::Question.new(:text => "Question #3")
95
- @survey.build_question(@q3)
97
+ @q3 = @survey.questions.build(:type => "ActiveRecordSurvey::Node::Question", :text => "Question #3", :survey => @survey)
96
98
  @q3_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 Answer #1")
97
99
  @q3_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q3 Answer #2")
98
- @q3.build_answer(@q3_a1, @survey)
99
- @q3.build_answer(@q3_a2, @survey)
100
+ @q3.build_answer(@q3_a1)
101
+ @q3.build_answer(@q3_a2)
100
102
 
101
- @q4 = ActiveRecordSurvey::Node::Question.new(:text => "Question #4")
102
- @survey.build_question(@q4)
103
+ @q4 = @survey.questions.build(:type => "ActiveRecordSurvey::Node::Question", :text => "Question #4", :survey => @survey)
103
104
  @q4_a1 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 Answer #1")
104
105
  @q4_a2 = ActiveRecordSurvey::Node::Answer.new(:text => "Q4 Answer #2")
105
- @q4.build_answer(@q4_a1, @survey)
106
- @q4.build_answer(@q4_a2, @survey)
106
+ @q4.build_answer(@q4_a1)
107
+ @q4.build_answer(@q4_a2)
107
108
 
108
109
  # Link up Q1
109
110
  @q1_a1.build_link(@q2)
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "activerecord", [">= 3.0", "< 5.0"]
22
- spec.add_dependency "awesome_nested_set", [">= 2.0"]
22
+ spec.add_dependency "awesome_nested_set", [">= 3.0"]
23
23
  if RUBY_PLATFORM == 'java'
24
24
  spec.add_development_dependency "jdbc-sqlite3", "> 0"
25
25
  spec.add_development_dependency "activerecord-jdbcsqlite3-adapter", "> 0"
@@ -12,7 +12,7 @@ module ActiveRecordSurvey
12
12
  instance_node.errors[:base] << "INVALID_PATH"
13
13
  end
14
14
 
15
- parent_nodes = self.node.node_maps.collect { |j| j.parent }
15
+ parent_nodes = self.node.survey.node_maps.select { |i| i.node == self.node }.collect { |j| j.parent }
16
16
 
17
17
  # Two instance_nodes on the same node for this instance
18
18
  if self.instance.instance_nodes.select { |i|
@@ -21,7 +21,7 @@ module ActiveRecordSurvey
21
21
  }.select { |i|
22
22
  # And the two arrays
23
23
  # Two votes share a parent (this means a question has two answers for this instance)
24
- (i.node.node_maps.collect { |j| j.parent } & parent_nodes).length > 0
24
+ (i.node.survey.node_maps.select { |j| i.node == j.node }.collect { |j| j.parent } & parent_nodes).length > 0
25
25
  }.length > 1
26
26
  instance_node.errors[:base] << "DUPLICATE_PATH"
27
27
  end
@@ -2,19 +2,24 @@ module ActiveRecordSurvey
2
2
  class Answer
3
3
  module Chained
4
4
  # Chain nodes are different - they must find the final answer node added and add to it
5
- def build_answer(question_node, survey)
5
+ def build_answer(question_node)
6
+ self.survey = question_node.survey
7
+
8
+ question_node_maps = self.survey.node_maps.select { |i| i.node == question_node && !i.marked_for_destruction? }
9
+
6
10
  # No node_maps exist yet from this question
7
- if question_node.node_maps.length === 0
11
+ if question_node_maps.length === 0
8
12
  # Build our first node-map
9
- question_node.node_maps.build(:node => question_node, :survey => survey)
13
+ question_node_maps << self.survey.node_maps.build(:node => question_node, :survey => self.survey)
10
14
  end
11
15
 
12
- last_in_chain = question_node.answers.last || question_node
16
+ last_answer_in_chain = (question_node.answers.last || question_node)
13
17
 
14
18
  # Each instance of this question needs the answer hung from it
15
- last_in_chain.node_maps.each { |node_map|
16
- answer_node_map = self.node_maps.build(:node => self, :survey => survey)
17
- node_map.children << answer_node_map
19
+ self.survey.node_maps.select { |i|
20
+ i.node == last_answer_in_chain
21
+ }.each { |node_map|
22
+ node_map.children << self.survey.node_maps.build(:node => self, :survey => self.survey)
18
23
  }
19
24
 
20
25
  true
@@ -4,14 +4,18 @@ module ActiveRecordSurvey
4
4
  # Validate this node against an instance
5
5
  def validate_node(instance)
6
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|
7
+ !self.survey.node_maps.select { |i|
8
+ i.node == self
9
+ }.collect { |node_map|
8
10
  node_map.parent.node.validate_node(instance)
9
11
  }.include?(false)
10
12
  end
11
13
 
12
14
  # Returns the question that preceeds this answer
13
15
  def question
14
- self.node_maps.collect { |node_map|
16
+ self.survey.node_maps.select { |i|
17
+ i.node == self
18
+ }.collect { |node_map|
15
19
  if node_map.parent && node_map.parent.node
16
20
  # Question is not the next parent - recurse!
17
21
  if node_map.parent.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Answer)
@@ -28,9 +32,11 @@ module ActiveRecordSurvey
28
32
 
29
33
  # Returns the question that follows this answer
30
34
  def next_question
31
- self.node_maps.each { |answer_node_map|
35
+ self.survey.node_maps.select { |i|
36
+ i.node == self && !i.marked_for_destruction?
37
+ }.each { |answer_node_map|
32
38
  answer_node_map.children.each { |child|
33
- if !child.node.nil?
39
+ if !child.node.nil? && !child.marked_for_destruction?
34
40
  if child.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
35
41
  return child.node
36
42
  elsif child.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Answer)
@@ -44,40 +50,32 @@ module ActiveRecordSurvey
44
50
  return nil
45
51
  end
46
52
 
47
- attr_accessor :ancestor_marked_for_destruction
48
- protected :ancestor_marked_for_destruction
49
-
50
- before_save do |node|
51
- # ------------------------ WARNING ------------------------
52
- # This code is to support #remove_link which uses mark_for_destruction
53
- # This code is necessary to clean everything up.
54
- # Calling save on this answer won't automatically go to its next_question -> node_maps and clean everything up
55
- (@ancestor_marked_for_destruction || []).each { |i|
56
- i.destroy
57
- }
58
- end
59
-
60
53
  # Removes the link
61
54
  def remove_link
62
- @ancestor_marked_for_destruction ||= []
63
-
64
55
  # not linked to a question - nothing to remove!
65
56
  return true if (question = self.next_question).nil?
66
57
 
67
- self.node_maps.each_with_index { |answer_nm, answer_nm_i|
68
- answer_nm.children = answer_nm.children.select { |question_nm|
69
- # This node_map links to the question
70
- if question_nm.node === question
71
- question_nm.parent = nil
72
- if answer_nm_i > 0
73
- question_nm.mark_for_destruction
74
- @ancestor_marked_for_destruction << question_nm
75
- end
76
- false
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)
77
64
  else
78
- true
65
+ node_map.parent = nil
79
66
  end
80
- }
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
81
79
  }
82
80
  end
83
81
 
@@ -89,48 +87,47 @@ module ActiveRecordSurvey
89
87
  raise ArgumentError.new "to_node must inherit from ::ActiveRecordSurvey::Node::Question"
90
88
  end
91
89
 
90
+ if self.question.nil?
91
+ raise ArgumentError.new "A question is required before calling #build_link"
92
+ end
93
+
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
+
92
100
  # Answer has already got a question - throw error
93
- if self.node_maps.select { |i|
101
+ if from_node_maps.select { |i|
94
102
  i.children.length === 0
95
103
  }.length === 0
96
104
  raise RuntimeError.new "This answer has already been linked"
97
105
  end
98
106
 
99
- # Attempt to find an unused to_node node_map
100
- if !(to_without_parent = to_node.node_maps.select { |i| i.parent.nil? }.first)
101
- # no unused path exists
102
- # we need to recursively clone the existing path
103
-
104
- to_node_map = to_node.node_maps.first || to_node.node_maps.build(:node => to_node, :survey => self.node_maps.first.survey)
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? }
105
109
 
106
- # Add it to all the from_node_maps
107
- self.node_maps.each { |from_node_map|
108
- from_node_map.children << to_node_map.recursive_clone
109
- }
110
- else
111
- # Find the node map from this node that has no children
112
- from_with_no_children = self.node_maps.select { |i|
113
- i.children.length == 0
114
- }
110
+ if to_node_maps.first.nil?
111
+ to_node_maps << self.survey.node_maps.build(:survey => self.survey, :node => to_node)
112
+ end
115
113
 
116
- if from_with_no_children.length > 1
117
- to_node_map = to_node.node_maps.first || to_node.node_maps.build(:node => to_node, :survey => self.node_maps.first.survey)
118
- end
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
119
117
 
120
- from_with_no_children.each_with_index { |from_with_no_children, index|
121
- # Use up the node that hasn't been used yet
122
- if index === 0
123
- from_with_no_children.children << to_without_parent
124
- # We need to clone destinations for each of the subsequent
125
- else
126
- from_with_no_children.children << to_node_map.recursive_clone
127
- end
128
- }
129
-
130
- # TODO - check to make sure there is no path to itself
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)
131
122
  end
132
123
 
133
- self.node_maps.each { |node_map|
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|
134
131
  # There is a path from Q -> A that is a loop
135
132
  if node_map.has_infinite_loop?
136
133
  raise RuntimeError.new "Infinite loop detected"
@@ -138,24 +135,27 @@ module ActiveRecordSurvey
138
135
  }
139
136
  end
140
137
 
141
- # By default - answers build off the original question node
142
- #
143
- # This allows us to easily override the answer building behaviour for different answer types
144
- #
145
- def build_answer(question_node, survey)
146
- # No node_maps exist yet from this question
147
- if question_node.node_maps.length === 0
148
- # Build our first node-map
149
- question_node.node_maps.build(:node => question_node, :survey => survey)
150
- end
138
+ private
139
+ # By default - answers build off the original question node
140
+ #
141
+ # This allows us to easily override the answer building behaviour for different answer types
142
+ def build_answer(question_node)
143
+ self.survey = question_node.survey
151
144
 
152
- # Each instance of this question needs the answer hung from it
153
- question_node.node_maps.each { |question_node_map|
154
- answer_node_map = self.node_maps.build(:node => self, :survey => survey)
155
- question_node_map.children << answer_node_map
156
- }
145
+ question_node_maps = self.survey.node_maps.select { |i| i.node == question_node && !i.marked_for_destruction? }
157
146
 
158
- true
159
- end
147
+ # No node_maps exist yet from this question
148
+ if question_node_maps.length === 0
149
+ # Build our first node-map
150
+ question_node_maps << self.survey.node_maps.build(:node => question_node, :survey => self.survey)
151
+ end
152
+
153
+ # Each instance of this question needs the answer hung from it
154
+ question_node_maps.each { |question_node_map|
155
+ question_node_map.children << self.survey.node_maps.build(:node => self, :survey => self.survey)
156
+ }
157
+
158
+ true
159
+ end
160
160
  end
161
161
  end
@@ -7,28 +7,15 @@ module ActiveRecordSurvey
7
7
  }.include?(false)
8
8
  end
9
9
 
10
- # Returns the survey to the question
11
- def survey
12
- if node_map = self.node_maps.first
13
- node_map.survey
14
- end
15
- end
16
-
17
10
  # Build an answer off this node
18
- def build_answer(answer_node, survey = nil)
19
- survey = survey || self.node_maps.select { |i|
20
- !i.survey.nil?
21
- }.collect { |i|
22
- i.survey
23
- }.first
24
-
25
- # A survey must either be passed or already present in node_maps
26
- if survey.nil?
11
+ def build_answer(answer_node)
12
+ # A survey must either be passed or already present in self.node_maps
13
+ if self.survey.nil?
27
14
  raise ArgumentError.new "A survey must be passed if Question is not yet added to a survey"
28
15
  end
29
16
 
30
- # Answers actually define how they're built off the parent node... yep
31
- answer_node.build_answer(self, survey)
17
+ # Answers actually define how they're built off the parent node
18
+ answer_node.send(:build_answer, self)
32
19
  end
33
20
  end
34
21
  end
@@ -1,13 +1,16 @@
1
1
  module ActiveRecordSurvey
2
2
  class Node < ::ActiveRecord::Base
3
3
  self.table_name = "active_record_survey_nodes"
4
+ belongs_to :survey, :class_name => "ActiveRecordSurvey::Survey", :foreign_key => :active_record_survey_id
4
5
  has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_node_id, autosave: true
5
6
  has_many :node_validations, :class_name => "ActiveRecordSurvey::NodeValidation", :foreign_key => :active_record_survey_node_id, autosave: true
6
7
  has_many :instance_nodes, :class_name => "ActiveRecordSurvey::InstanceNode", :foreign_key => :active_record_survey_node_id
7
8
 
8
9
  # All the answer nodes that follow from this node
9
10
  def answers
10
- self.node_maps.collect { |i|
11
+ self.survey.node_maps.select { |i|
12
+ i.node == self
13
+ }.collect { |i|
11
14
  # Get all the children from this node
12
15
  i.children
13
16
  }.flatten.collect { |i|
@@ -17,6 +20,7 @@ module ActiveRecordSurvey
17
20
  # Only the nodes that are answers
18
21
  i.class.ancestors.include?(::ActiveRecordSurvey::Node::Answer)
19
22
  }.uniq.collect { |i|
23
+ i.survey = self.survey # ensure that the survey being referenced by the answers is the original survey - needed for keeping consistent node_maps between build_link and remove_link
20
24
  [i] + i.answers
21
25
  }.flatten.uniq
22
26
  end
@@ -48,7 +52,7 @@ module ActiveRecordSurvey
48
52
 
49
53
  # Default behaviour is to recurse up the chain (goal is to hit a question node)
50
54
  def validate_parent_instance_node(instance_node, child_node)
51
- !self.node_maps.collect { |node_map|
55
+ !self.survey.node_maps.select { |i| i.node == self}.collect { |node_map|
52
56
  if node_map.parent
53
57
  node_map.parent.node.validate_parent_instance_node(instance_node, self)
54
58
  # Hit top node
@@ -72,7 +76,7 @@ module ActiveRecordSurvey
72
76
  # More complex....
73
77
  # Recureses to the parent node to check
74
78
  # This is to validate Node::Question since they don't have instance_nodes directly to validate them
75
- parent_validations_passed = !self.node_maps.collect { |node_map|
79
+ parent_validations_passed = !self.survey.node_maps.select { |i| i.node == self}.collect { |node_map|
76
80
  if node_map.parent
77
81
  node_map.parent.node.validate_parent_instance_node(instance_node, self)
78
82
  # Hit top node
@@ -96,7 +100,7 @@ module ActiveRecordSurvey
96
100
 
97
101
  # Start at each node_map of this node
98
102
  # Find the parent node ma
99
- paths = self.node_maps.collect { |node_map|
103
+ paths = self.survey.node_maps.select { |i| i.node == self }.collect { |node_map|
100
104
  # There is another level to traverse
101
105
  if node_map.parent
102
106
  node_map.parent.node.instance_node_path_to_root?(instance_node)
@@ -7,33 +7,34 @@ module ActiveRecordSurvey
7
7
 
8
8
  validates_presence_of :survey
9
9
 
10
- after_initialize do |i|
11
- # Required for all functions to work without creating
12
- i.survey.node_maps << self if i.new_record? && i.survey
13
- end
14
-
15
10
  # Recursively creates a copy of this entire node_map
16
11
  def recursive_clone
17
- node_map = self.node.node_maps.build(:survey => self.survey, :node => self.node)
12
+ node_map = self.survey.node_maps.build(:survey => self.survey, :node => self.node)
18
13
  self.children.each { |child_node|
14
+ child_node.survey = self.survey # required due to voodoo - we want to use the same survey with the same object_id
19
15
  node_map.children << child_node.recursive_clone
20
16
  }
21
17
  node_map
22
18
  end
23
19
 
24
- def as_map(node_maps = nil)
25
- children = (node_maps.nil?)? self.children : node_maps.select { |i|
26
- i.parent == self
20
+ def as_map(options)
21
+ node_maps = options[:node_maps]
22
+
23
+ c = (node_maps.nil?)? self.children : node_maps.select { |i|
24
+ i.parent == self && !i.marked_for_destruction?
27
25
  }
28
26
 
29
- {
30
- :id => self.id,
31
- :node_id => self.node.id,
27
+ result = {}
28
+ result.merge!({ :id => self.id, :node_id => self.node.id }) if !options[:no_ids]
29
+ result.merge!({
32
30
  :type => self.node.class.to_s,
33
- :children => children.collect { |i|
34
- i.as_map(node_maps)
31
+ :children => c.collect { |i|
32
+ i.as_map(options)
35
33
  }
36
- }
34
+ })
35
+
36
+
37
+ result
37
38
  end
38
39
 
39
40
  # Gets all the child nodes until one is not an ancestor of klass
@@ -57,5 +58,14 @@ module ActiveRecordSurvey
57
58
  }
58
59
  false
59
60
  end
61
+
62
+ def mark_self_and_children_for_destruction
63
+ removed = [self]
64
+ self.mark_for_destruction
65
+ self.children.each { |i|
66
+ removed.concat(i.mark_self_and_children_for_destruction)
67
+ }
68
+ removed
69
+ end
60
70
  end
61
71
  end
@@ -1,44 +1,21 @@
1
1
  module ActiveRecordSurvey
2
2
  class Survey < ::ActiveRecord::Base
3
3
  self.table_name = "active_record_surveys"
4
- has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_id
4
+ has_many :node_maps, :class_name => "ActiveRecordSurvey::NodeMap", :foreign_key => :active_record_survey_id, autosave: true
5
5
  has_many :nodes, -> { distinct }, :through => :node_maps
6
-
7
- def questions
8
- self.node_maps.includes(:node).select { |i|
9
- i.node.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
10
- }.collect { |i|
11
- i.node
12
- }.uniq
13
- end
6
+ has_many :questions, :class_name => "ActiveRecordSurvey::Node::Question", :foreign_key => :active_record_survey_id
14
7
 
15
8
  def root_node
16
9
  self.node_maps.includes(:node).select { |i| i.depth === 0 }.first
17
10
  end
18
11
 
19
- def as_map
20
- list = self.node_maps
12
+ def as_map(*args)
13
+ options = args.extract_options!
14
+ options[:node_maps] ||= self.node_maps
21
15
 
22
- list.select { |i| !i.parent }.collect { |i|
23
- i.as_map(list)
16
+ self.node_maps.select { |i| !i.parent && !i.marked_for_destruction? }.collect { |i|
17
+ i.as_map(options)
24
18
  }
25
19
  end
26
-
27
- # Build a question for this survey
28
- def build_question(question)
29
- # build_question only accepts a node that inherits from Question
30
- if !question.class.ancestors.include?(::ActiveRecordSurvey::Node::Question)
31
- raise ArgumentError.new "Question must inherit from ::ActiveRecordSurvey::Node::Question"
32
- end
33
-
34
- # Already added - shouldn't add twice
35
- if question.node_maps.select { |node_map|
36
- node_map.survey === self
37
- }.length > 0
38
- raise RuntimeError.new "This question has already been added to the survey"
39
- end
40
-
41
- question.node_maps.build(:node => question, :survey => self)
42
- end
43
20
  end
44
21
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordSurvey
2
- VERSION = "0.1.25"
2
+ VERSION = "0.1.26"
3
3
  end
@@ -25,6 +25,8 @@ class AddActiveRecordSurvey < ActiveRecord::Migration
25
25
  t.integer :parent_id, :null => true, :index => true
26
26
  t.integer :lft, :null => false, :index => true
27
27
  t.integer :rgt, :null => false, :index => true
28
+
29
+ # optional fields
28
30
  t.integer :depth, :null => false, :default => 0
29
31
  t.integer :children_count, :null => false, :default => 0
30
32
 
@@ -0,0 +1,9 @@
1
+ class Update_0_1_26_ActiveRecordSurvey < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :active_record_survey_nodes, :active_record_survey_id, :integer
4
+ end
5
+
6
+ def self.down
7
+ remove_column :active_record_survey_nodes, :active_record_survey_id
8
+ end
9
+ end
@@ -3,21 +3,20 @@ require 'spec_helper'
3
3
  describe ActiveRecordSurvey::Node::Answer::Boolean, :boolean_spec => true do
4
4
  describe 'a boolean survey is' do
5
5
  before(:all) do
6
- @survey = ActiveRecordSurvey::Survey.new
7
-
8
- @q1 = ActiveRecordSurvey::Node::Question.new()
9
- @q1_a1 = ActiveRecordSurvey::Node::Answer::Boolean.new()
10
- @q1_a2 = ActiveRecordSurvey::Node::Answer::Boolean.new()
11
- @q1_a3 = ActiveRecordSurvey::Node::Answer::Boolean.new()
12
- @q1_a4 = ActiveRecordSurvey::Node::Answer::Boolean.new()
13
- @q1_a5 = ActiveRecordSurvey::Node::Answer::Boolean.new()
14
-
15
- @survey.build_question(@q1)
16
- @q1.build_answer(@q1_a1, @survey)
17
- @q1.build_answer(@q1_a2, @survey)
18
- @q1.build_answer(@q1_a3, @survey)
19
- @q1.build_answer(@q1_a4, @survey)
20
- @q1.build_answer(@q1_a5, @survey)
6
+ @survey = ActiveRecordSurvey::Survey.new()
7
+
8
+ @q1 = ActiveRecordSurvey::Node::Question.new(:text => "Q1", :survey => @survey)
9
+ @q1_a1 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "A")
10
+ @q1_a2 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "B")
11
+ @q1_a3 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "C")
12
+ @q1_a4 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "D")
13
+ @q1_a5 = ActiveRecordSurvey::Node::Answer::Boolean.new(:text => "E")
14
+
15
+ @q1.build_answer(@q1_a1)
16
+ @q1.build_answer(@q1_a2)
17
+ @q1.build_answer(@q1_a3)
18
+ @q1.build_answer(@q1_a4)
19
+ @q1.build_answer(@q1_a5)
21
20
 
22
21
  @survey.save
23
22
  end
@@ -5,19 +5,18 @@ describe ActiveRecordSurvey::Node::Answer::Rank, :rank_spec => true do
5
5
  before(:all) do
6
6
  @survey = ActiveRecordSurvey::Survey.new
7
7
 
8
- @q1 = ActiveRecordSurvey::Node::Question.new()
8
+ @q1 = ActiveRecordSurvey::Node::Question.new(:survey => @survey)
9
9
  @q1_a1 = ActiveRecordSurvey::Node::Answer::Rank.new()
10
10
  @q1_a2 = ActiveRecordSurvey::Node::Answer::Rank.new()
11
11
  @q1_a3 = ActiveRecordSurvey::Node::Answer::Rank.new()
12
12
  @q1_a4 = ActiveRecordSurvey::Node::Answer::Rank.new()
13
13
  @q1_a5 = ActiveRecordSurvey::Node::Answer::Rank.new()
14
14
 
15
- @survey.build_question(@q1)
16
- @q1.build_answer(@q1_a1, @survey)
17
- @q1.build_answer(@q1_a2, @survey)
18
- @q1.build_answer(@q1_a3, @survey)
19
- @q1.build_answer(@q1_a4, @survey)
20
- @q1.build_answer(@q1_a5, @survey)
15
+ @q1.build_answer(@q1_a1)
16
+ @q1.build_answer(@q1_a2)
17
+ @q1.build_answer(@q1_a3)
18
+ @q1.build_answer(@q1_a4)
19
+ @q1.build_answer(@q1_a5)
21
20
 
22
21
  @survey.save
23
22
  end