doop 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +203 -0
  6. data/Rakefile +5 -0
  7. data/app/assets/images/dash.gif +0 -0
  8. data/app/assets/images/info-icon.png +0 -0
  9. data/app/assets/images/progress-tick-large.png +0 -0
  10. data/app/helpers/doop_helper.rb +51 -0
  11. data/bin/.gitignore +0 -0
  12. data/doop.gemspec +26 -0
  13. data/lib/doop.rb +313 -0
  14. data/lib/doop/version.rb +3 -0
  15. data/lib/doop_controller.rb +164 -0
  16. data/lib/generators/doopgovuk/USAGE +10 -0
  17. data/lib/generators/doopgovuk/doopgovuk_generator.rb +34 -0
  18. data/lib/generators/doopgovuk/templates/app/assets/stylesheets/demo.css.scss +188 -0
  19. data/lib/generators/doopgovuk/templates/app/controllers/demo_controller.rb +155 -0
  20. data/lib/generators/doopgovuk/templates/app/views/demo/_preamble.html.erb +33 -0
  21. data/lib/generators/doopgovuk/templates/app/views/demo/_summary.html.erb +10 -0
  22. data/lib/generators/doopgovuk/templates/app/views/demo/_your_details.html.erb +55 -0
  23. data/lib/generators/doopgovuk/templates/app/views/demo/index.html.erb +3 -0
  24. data/lib/generators/doopgovuk/templates/app/views/demo/index.js.erb +4 -0
  25. data/lib/generators/doopgovuk/templates/app/views/doop/_error.html.erb +3 -0
  26. data/lib/generators/doopgovuk/templates/app/views/doop/_info_box.html.erb +4 -0
  27. data/lib/generators/doopgovuk/templates/app/views/doop/_navbar.html.erb +23 -0
  28. data/lib/generators/doopgovuk/templates/app/views/doop/_question.html.erb +19 -0
  29. data/lib/generators/doopgovuk/templates/app/views/doop/_question_form.html.erb +15 -0
  30. data/lib/generators/doopgovuk/templates/app/views/layouts/application.html.erb +22 -0
  31. data/spec/doop_spec.rb +448 -0
  32. data/spec/spec_helper.rb +2 -0
  33. metadata +136 -0
@@ -0,0 +1,33 @@
1
+ <%=question_form doop, res do %>
2
+ <%=question "/page/preamble" do |root, answer| %>
3
+
4
+ <%=question "/page/preamble/debug_on" do |root,answer| %>
5
+ <button name="b_answer" value="Yes">Yes, turn debugging on</button><br/>
6
+ <button name="b_answer" value="No">No, turn debugging off</button>
7
+ <% end %>
8
+
9
+ <%=question "/page/preamble/enrolled_before" do |root,answer| %>
10
+ <button name="b_answer" value="Yes">Yes, I've applied before</button><br/>
11
+ <button name="b_answer" value="No">No, I haven't applied before</button>
12
+ <% end %>
13
+
14
+ <%=question "/page/preamble/year_last_applied" do |root,answer| %>
15
+ <%=select_tag( "b_answer", "<option>2013</option><option>2012</option>".html_safe ) %>
16
+ <br/>
17
+ <button>Continue</button>
18
+ <% end %>
19
+
20
+ <%=question "/page/preamble/reason_for_applying" do |root,answer| %>
21
+ <p>Why are you applying this year? Please be as specific as possible, and give examples</p>
22
+ <%=error res, :reason_for_applying_error %>
23
+ <%=text_area_tag "b_answer", answer, rows: 10, cols: 100 %>
24
+ <br/>
25
+ <button>Continue</button>
26
+ <% end %>
27
+
28
+ <% when_answered "/page/preamble" do %>
29
+ <button>Continue and Save</button>
30
+ <% end %>
31
+ <% end %>
32
+
33
+ <% end %>
@@ -0,0 +1,10 @@
1
+
2
+ <%=question_form doop, res do %>
3
+ <%=question "/page/summary" do |root, answer| %>
4
+ <%=question "/page/summary/terms_and_conditions" do |root, answer| %>
5
+ <p>I have answered the questions to the best of my knowledge and agree to abide by the terms and conditions of this service.</p>
6
+ <button>I have read and understood the terms and conditions</button>
7
+ <% end %>
8
+
9
+ <% end %>
10
+ <% end %>
@@ -0,0 +1,55 @@
1
+ <%=question_form doop, res do %>
2
+ <%=question "/page/your_details" do |root, answer| %>
3
+
4
+ <%=question "/page/your_details/your_name" do |root,answer| %>
5
+ <div class="textfield">
6
+ <label for="firstname">First names</label>
7
+ <input id="firstname" name="b_firstname" type="text" value="<%=answer["firstname"]%>"/>
8
+ </div>
9
+ <div class="textfield">
10
+ <label for="surname">Surname</label>
11
+ <input id="surname" name="b_surname" type="text" value="<%=answer["surname"]%>"/>
12
+ <br/>
13
+ <button>Continue</button><br/>
14
+ <% end %>
15
+
16
+ <%=question "/page/your_details/address_history" do |root,answer| %>
17
+
18
+ <p>We need to know where you have lived for the past 6 years</p>
19
+
20
+ <% list "/page/your_details/address_history/address__(\\d+)" do |path,index| %>
21
+ <%=question path, :title => "Address #{index}" do |root,answer| %>
22
+ <div class="textfield">
23
+ <label for="address1">Address</label>
24
+ <input id="address1" type="text" name="b_address1" value="<%=answer['address1']%>"/>
25
+ </div>
26
+ <div class="textfield">
27
+ <input id="address2" type="text" name="b_address2" value="<%=answer['address2']%>"/>
28
+ </div>
29
+
30
+ <div class="textfield">
31
+ <input id="address3" type="text" name="b_address3" value="<%=answer['address3']%>"/>
32
+ </div>
33
+ <div class="textfield">
34
+ <label for="postcode">Postcode</label>
35
+ <input id="postcode" type="text" name="b_postcode" value="<%=answer['postcode']%>"/>
36
+ </div>
37
+ <% if index > 1 %>
38
+ <button class="button-secondary" name="remove_address">Remove</button><br/>
39
+ <% end %>
40
+ <button>Continue</button>
41
+ <% end %>
42
+ <% end %>
43
+ <% when_answered "/page/your_details/address_history" do %>
44
+ <button name="add_address" class="button-secondary">Add another address</button><br/>
45
+ <button>Continue</button>
46
+ <% end %>
47
+
48
+ <% end %>
49
+
50
+ <% when_answered "/page/your_details" do %>
51
+ <button>Continue and Save</button>
52
+ <% end %>
53
+ <% end %>
54
+
55
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <div id="question_section">
2
+ <%= render res[:page], :doop => doop, :res => res %>
3
+ </div>
@@ -0,0 +1,4 @@
1
+ $("#question_section").html( "<%= escape_javascript( render res[:page], :doop => doop, :res => res ) %>" )
2
+ $("#nav_path").val("")
3
+ $("#back_a_page").val("")
4
+ $("#change_answer").val("")
@@ -0,0 +1,3 @@
1
+ <div class="error">
2
+ <span><%= msg %></span>
3
+ </div>
@@ -0,0 +1,4 @@
1
+ <div class="info_box">
2
+ <%=image_tag "info-icon.png", :class => "info_img" %>
3
+ <% content.call %>
4
+ </div>
@@ -0,0 +1,23 @@
1
+ <div class="navbar">
2
+ <ol>
3
+ <%
4
+ i = 1
5
+ pages = res[:all_pages]
6
+ page_path = res[:page_path]
7
+ pages.each do |p|
8
+ nav_name = doop[p]["_nav_name"]
9
+
10
+ if p == page_path %>
11
+ <li class="doing"><%="#{i}. #{nav_name}"%></li>
12
+ <% elsif doop[p]["_answered"] %>
13
+ <li class="done"><a href="#" onclick="$('#nav_path').val('<%=p %>').click(); return false"><%="#{i}. #{nav_name}" %></a></li>
14
+ <% else %>
15
+ <li class="todo"><%="#{i}. #{nav_name}"%></li>
16
+ <%
17
+ end
18
+ i +=1
19
+ end %>
20
+ </ol>
21
+
22
+ </div>
23
+
@@ -0,0 +1,19 @@
1
+ <% if root["_open"] %>
2
+ <% if indent %><div class="indent"><% end %>
3
+ <div class="question-open">
4
+ <h2><%=title != nil ? title : root["_question"]%></h2>
5
+ <% content.call(root, answer) %>
6
+ </div>
7
+ <% if indent %> </div"><% end %>
8
+ <% end %>
9
+
10
+ <% if !root["_open"] && root["_answered"] %>
11
+ <% if indent %><div class="indent"><% end %>
12
+ <div class="question-closed">
13
+ <div class="title"><%=title != nil ? title : root["_question"]%></div>
14
+ <div class="answer">
15
+ <a href="#" class="button" onclick="$('#change_answer_path').val('<%=path %>').click(); return false"><%=root['_summary']%></a>
16
+ </div>
17
+ </div>
18
+ <% if indent %> </div"><% end %>
19
+ <% end %>
@@ -0,0 +1,15 @@
1
+ <%=form_tag( {action: "answer"}, :authenticity_token => true, :remote => true ) do %>
2
+ <div class="question_form">
3
+ <%=render "doop/navbar", :res => res, :doop => doop %>
4
+ <h1><%=res[:page_title]%></h1>
5
+ <% content.call %>
6
+ </div>
7
+
8
+ <% if res[:debug_on] %>
9
+ <br/><br/><textarea cols="115" rows="20"><%= h(doop.dump)%></textarea>
10
+ <% end %>
11
+ <button id="nav_path" style="display: none;" name="nav_path"></button>
12
+ <button id="back_a_page" name="back_a_page" style="display: none;"></button>
13
+ <button id="change_answer_path" style="display: none;" name="change_answer_path"></button>
14
+ <input type="hidden" name="doop_data" value="<%=request["doop_data"]%>"/>
15
+ <% end %>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+
3
+ <% content_for :head do %>
4
+ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
5
+ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
6
+ <%= csrf_meta_tags %>
7
+ <% end %>
8
+
9
+ <% content_for :content do %>
10
+ <div class="outer-block">
11
+ <div class="inner-block">
12
+ <div class="phase-banner">
13
+ <strong class="phase-tag">BETA</strong>
14
+ <span>This is a new service – your <a href="#">feedback</a> will help us to improve it.</span>
15
+ </div>
16
+
17
+ <%= yield %>
18
+ </div>
19
+ </div>
20
+ <% end %>
21
+
22
+ <%= render template: "layouts/govuk_template" %>
@@ -0,0 +1,448 @@
1
+ require "spec_helper"
2
+
3
+ describe "Doop" do
4
+
5
+ describe "automatic binding" do
6
+
7
+ let(:question) {
8
+ q = Doop::Doop.new
9
+ q.yaml = (
10
+ <<-EOS
11
+
12
+ root: {
13
+ age: {},
14
+ address: {
15
+ address_line_1: { _answer: "21 The Grove" },
16
+ address_line_2: { _answer: "Telford" },
17
+ address_line_3: {}
18
+ },
19
+
20
+ pets: {
21
+ pet__8: { _answer: "Claude" },
22
+ pet__3: { _answer: "Lucy" }
23
+ }
24
+
25
+ }
26
+
27
+ EOS
28
+ )
29
+ q.init
30
+ q.ask_next
31
+ q
32
+ }
33
+
34
+
35
+ it "attempts to bind anything starting with b_ to the answer" do
36
+ expect(question.currently_asked).to eq( "/root/age" )
37
+ question.answer( { "b_age"=>10 } )
38
+ expect( question["/root/age/_answer"]["age"] ).to eq(10)
39
+
40
+ end
41
+
42
+ end
43
+
44
+ describe "structure management" do
45
+
46
+ let(:question) {
47
+ q = Doop::Doop.new
48
+ q.yaml =
49
+ <<-EOS
50
+
51
+ root: {
52
+ age: {_answer: 25, _answered: true},
53
+ address: {
54
+ address_line_1: { _answer: "21 The Grove" },
55
+ address_line_2: { _answer: "Telford" },
56
+ address_line_3: {}
57
+ },
58
+
59
+ pets: {
60
+ pet__8: { _answer: "Claude" },
61
+ pet__3: { _answer: "Lucy" }
62
+ }
63
+
64
+ }
65
+
66
+ EOS
67
+ q.init
68
+ q.ask_next
69
+ q
70
+ }
71
+
72
+ it "is initialized with a YAML data structure" do
73
+ end
74
+
75
+ it "can be serialized" do
76
+ dump = question.dump
77
+ expect(dump).to include( "root" )
78
+ expect(dump).to eq( Doop::Doop.new(dump).dump )
79
+ end
80
+
81
+ it "allows questions to be accessed like a file system" do
82
+ expect(question["/root/age/_answer"] ).to eq(25)
83
+ expect(question["/root/age"] ).not_to be_nil
84
+ expect(question["/root/address/address_line_1/_answer"] ).to eq("21 The Grove")
85
+ expect(question["/root/address/address_line_3/_answer"] ).to eq(nil)
86
+ end
87
+
88
+ it "allows questions to be answered" do
89
+ question["/root/address/address_line_3/_answer"] = "Shropshire"
90
+ expect(question["/root/address/address_line_3/_answer"] ).to eq("Shropshire")
91
+
92
+ yaml = <<-EOS
93
+
94
+ address_line_1: { _answer: "AAA" }
95
+ address_line_2: { _answer: "BBB" }
96
+
97
+ EOS
98
+
99
+ question["/root/address"] = YAML.load(yaml)
100
+ expect(question["/root/address/address_line_1/_answer"] ).to eq("AAA")
101
+
102
+ end
103
+
104
+ it "allows questions to be added" do
105
+ question.add( "/root/address/address_line_4" )
106
+ question["/root/address/address_line_4/_answer"] = "XXX"
107
+ expect(question["/root/address/address_line_4/_answer"] ).to eq("XXX")
108
+ end
109
+
110
+ it "allows questions to be removed" do
111
+ question.remove( "/root/address/address_line_1" )
112
+ expect(question["/root/address/address_line_1"] ).to eq(nil)
113
+ end
114
+
115
+ it "allows questions to be moved" do
116
+ question.move( "/root/address/address_line_1", "/root/address/address_line_6" )
117
+ expect(question["/root/address/address_line_1"] ).to eq(nil)
118
+ expect(question["/root/address/address_line_6/_answer"] ).to eq("21 The Grove")
119
+ end
120
+
121
+ it "allows question to be renumbered in sequence" do
122
+ question.renumber( "/root/pets" )
123
+ expect(question["/root/pets/pet__1/_answer"] ).to eq("Lucy")
124
+ expect(question["/root/pets/pet__2/_answer"] ).to eq("Claude")
125
+ end
126
+
127
+ it "automatically adds meta data. Meta data starts with _" do
128
+
129
+ expect(question["/root/pets/_answered"] ).not_to be_nil
130
+ expect(question["/root/pets/_open"] ).not_to be_nil
131
+ expect(question["/root/pets/_enabled"] ).not_to be_nil
132
+ expect(question["/root/pets/_answer"] ).to be_nil
133
+
134
+ end
135
+
136
+ end
137
+
138
+
139
+ describe "navigation and answering" do
140
+
141
+
142
+ let(:question) {
143
+ q = Doop::Doop.new
144
+ q.yaml=
145
+ <<-EOS
146
+
147
+ root: {
148
+ age: { _question: "How old are you?"},
149
+ address: {
150
+ _question: "What is your address",
151
+ address_line__1: { _question: "Address Line 1"},
152
+ address_line__2: { _question: "Address Line 2"},
153
+ address_line__3: { _question: "Address Line 3"}
154
+ }
155
+ }
156
+
157
+ EOS
158
+ q.init
159
+ q.ask_next
160
+ q
161
+ }
162
+
163
+ it "gets the next unaswered question" do
164
+ expect(question.currently_asked).to eq( "/root/age" )
165
+ end
166
+
167
+
168
+ it "allows question to be answered in order" do
169
+ expect(question.currently_asked).to eq( "/root/age" )
170
+ question.answer( { "answer" => 36 } )
171
+ expect(question["/root/age/_answer"]).to eq( 36 )
172
+ expect(question["/root/address/_open"]).to eq( true )
173
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
174
+ question.answer( { "answer" => "address1" } )
175
+ expect(question.currently_asked).to eq( "/root/address/address_line__2" )
176
+ question.answer( {"answer" => "address2" } )
177
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
178
+ question.answer( {"answer" => "address3" } )
179
+ expect(question.currently_asked).to eq( "/root/address" )
180
+ question.answer( { "answer" => "#{question['/root/address/address_line__1']}, #{question['/root/address/address_line__2']}, #{question['/root/address/address_line__3']}" } )
181
+ expect(question.currently_asked).to eq( "/root" )
182
+ question.answer( { "answer" => "done" } )
183
+ expect(question.currently_asked).to be_nil
184
+ end
185
+
186
+ it "allows questions to be disabled" do
187
+ question.disable("/root/address")
188
+ expect(question.currently_asked).to eq( "/root/age" )
189
+ question.answer( {"answer" => 36 } )
190
+ expect(question.currently_asked).to eq( "/root" )
191
+ end
192
+
193
+ it "allows earlier questions to be changed" do
194
+ expect(question.currently_asked).to eq( "/root/age" )
195
+ question.answer( {"answer" => 36} )
196
+ expect(question["/root/age/_answer"]).to eq( 36 )
197
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
198
+ question.answer( {"answer" => "address1" } )
199
+ expect(question.currently_asked).to eq( "/root/address/address_line__2" )
200
+ question.answer( {"answer" => "address2"} )
201
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
202
+ question.change( "/root/address/address_line__1" )
203
+ dump = question.dump
204
+ expect( question.dump ).to eq( Doop::Doop.new(dump).dump )
205
+
206
+
207
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
208
+ question.answer( {"answer" => "address1"} )
209
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
210
+
211
+ question.change( "/root/age" )
212
+ expect(question.currently_asked).to eq( "/root/age" )
213
+ question.answer({ "answer" => 35} )
214
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
215
+ end
216
+
217
+ it "provides a mechanism to see if all questions are answered under a given path" do
218
+
219
+ expect(question.currently_asked).to eq( "/root/age" )
220
+ question.answer( {"answer" => 36} )
221
+ expect(question["/root/age/_answer"]).to eq( 36 )
222
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
223
+ question.answer( {"answer" => "address1" } )
224
+ expect(question.currently_asked).to eq( "/root/address/address_line__2" )
225
+ question.answer( {"answer" => "address2"} )
226
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
227
+ question.answer({ "answer" => "address3"} )
228
+ expect(question.all_answered("/root/address")).to eq(true)
229
+ expect(question.all_answered("/root")).to eq(false)
230
+ question.answer({ "answer" => "provided"} )
231
+ expect(question.all_answered("/root")).to eq(true)
232
+ question.answer({ "answer" => "provided"} )
233
+ expect(question.all_answered("/")).to eq(true)
234
+ end
235
+
236
+ end
237
+
238
+ describe "on question answered callbacks" do
239
+
240
+ let(:question) {
241
+ q = Doop::Doop.new
242
+ q.yaml=
243
+ <<-EOS
244
+
245
+ root: {
246
+ address: {
247
+ _question: "What is your address",
248
+ address_line__1: { _question: "Address Line 1"},
249
+ address_line__2: { _question: "Address Line 2"},
250
+ address_line__3: { _question: "Address Line 3"}
251
+ },
252
+ age: {
253
+ _question: "How old are you?",
254
+ _on_answer_handler: "how_old_are_you"
255
+ }
256
+
257
+ }
258
+
259
+ EOS
260
+ q.init
261
+ q.ask_next
262
+ q
263
+
264
+ }
265
+
266
+ it "allows a callback when a question is answered. This allows flows to be changed based on the answer" do
267
+
268
+ question.on_answer "/root/address/address_line__1" do |root, path, context, answer|
269
+ question.enable( "/root/address/address_line__2", context[:button] == :ok )
270
+ root["_answer"] = "answered!"
271
+ root["_summary"] = "my summary"
272
+ root["_answered"] = true
273
+
274
+ end
275
+
276
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
277
+ question.answer( {:button => :skip} )
278
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
279
+ question.change( "/root/address/address_line__1" )
280
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
281
+ question.answer( {:button => :ok} )
282
+ expect(question.currently_asked).to eq( "/root/address/address_line__2" )
283
+ end
284
+
285
+ it "allows callbacks based on a regex expression" do
286
+ question.on_answer "/root/address/address_line__(.*)" do |root, path, context|
287
+ root["_answer"] = "answered!"
288
+ root["_answered"] = true
289
+ end
290
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
291
+ question.answer( {} )
292
+ expect(question["/root/address/address_line__1/_answer"]).to eq( "answered!" )
293
+ question.answer( {} )
294
+ expect(question["/root/address/address_line__2/_answer"]).to eq( "answered!" )
295
+ end
296
+
297
+ it "allows named handlers to be used" do
298
+ question.on_answer "how_old_are_you" do |root,path,context,answer|
299
+ root["_answered"] = true
300
+ end
301
+ question.answer( {} )
302
+ question.answer( {} )
303
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
304
+ question.answer( {"b_answer" => "test"} )
305
+ expect( question[ "/root/address/address_line__3/_answer" ] ).to eq( "test" )
306
+ question.answer( {} )
307
+ expect(question.currently_asked).to eq( "/root/age" )
308
+ question.answer( {"b_age" => 30 } )
309
+ expect(question["/root/age/_answer/age"]).to eq( 30 )
310
+ end
311
+
312
+ end
313
+
314
+ describe "on child question answered callbacks" do
315
+
316
+ let(:nested_question) {
317
+ q = Doop::Doop.new
318
+
319
+ q.yaml=
320
+ <<-EOS
321
+ root: {
322
+ a: {
323
+ b: {
324
+ c1: {},
325
+ c2: {}
326
+ }
327
+ }
328
+ }
329
+ EOS
330
+ q.init
331
+ q.ask_next
332
+ q
333
+
334
+ }
335
+
336
+ let(:question) {
337
+ q = Doop::Doop.new
338
+ q.yaml =
339
+ <<-EOS
340
+
341
+ root: {
342
+ address: {
343
+ _question: "What is your address",
344
+ address_line__1: { _question: "Address Line 1"},
345
+ address_line__2: { _question: "Address Line 2"},
346
+ address_line__3: { _question: "Address Line 3"}
347
+ },
348
+ age: {
349
+ _question: "How old are you?",
350
+ _on_answer_handler: "how_old_are_you"
351
+ }
352
+
353
+ }
354
+
355
+ EOS
356
+ q.init
357
+ q.ask_next
358
+ q
359
+
360
+ }
361
+
362
+ it "allows a callback when all nested questions have been answered" do
363
+ question.on_all_nested_answer "/root/address" do |root,path,context|
364
+ root["_answer"] = "answered!"
365
+ root["_answered"] = true
366
+ end
367
+
368
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
369
+ question.answer( {} )
370
+ expect(question.currently_asked).to eq( "/root/address/address_line__2" )
371
+ question.answer( {} )
372
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
373
+ question.answer( {} )
374
+ # /root/address should be answered by the callback
375
+ expect(question.currently_asked).to eq( "/root/age" )
376
+ end
377
+
378
+ it "recursively descends the question tree, making call backs" do
379
+ nested_question.on_all_nested_answer "/root/a/b" do |root,path,context|
380
+ root["_answer"] = "answered!"
381
+ root["_answered"] = true
382
+ end
383
+ nested_question.on_all_nested_answer "/root/a" do |root,path,context|
384
+ root["_answer"] = "answered!"
385
+ root["_answered"] = true
386
+ end
387
+
388
+ expect(nested_question.currently_asked).to eq( "/root/a/b/c1" )
389
+ nested_question.answer( {} )
390
+ expect(nested_question.currently_asked).to eq( "/root/a/b/c2" )
391
+ nested_question.answer( {} )
392
+ expect(nested_question.currently_asked).to eq( "/root" )
393
+ end
394
+
395
+ end
396
+
397
+ describe "on the fly structure manipulation" do
398
+
399
+ let(:question) {
400
+ q = Doop::Doop.new
401
+ q.yaml =
402
+ <<-EOS
403
+
404
+ root: {
405
+ address: {
406
+ _question: "What is your address",
407
+ address_line__1: { _question: "Address Line 1"},
408
+ address_line__2: { _question: "Address Line 2"},
409
+ address_line__3: { _question: "Address Line 3"}
410
+ },
411
+ age: {
412
+ _question: "How old are you?",
413
+ _on_answer_handler: "how_old_are_you"
414
+ }
415
+
416
+ }
417
+
418
+ EOS
419
+ q.init
420
+ q.ask_next
421
+ q
422
+
423
+ }
424
+
425
+ it "allows questions to be removed and added in flight" do
426
+
427
+ expect(question.currently_asked).to eq( "/root/address/address_line__1" )
428
+ question.answer( {"answer"=>"address1"} )
429
+ question.answer( {"answer"=>"address2"} )
430
+ question.answer( {"answer"=>"address3"} )
431
+ expect(question.currently_asked).to eq( "/root/address" )
432
+
433
+ expect( question["/root/address/address_line__1/_answer"] ).to eq( "address1" )
434
+
435
+ question.remove( "/root/address/address_line__1" )
436
+ question.renumber( "/root/address" )
437
+ question.ask_next
438
+ expect( question["/root/address/address_line__1/_answer"] ).to eq( "address2" )
439
+
440
+ question.add( "/root/address/address_line__3" )
441
+ question.ask_next
442
+ expect(question.currently_asked).to eq( "/root/address/address_line__3" )
443
+
444
+ end
445
+ end
446
+
447
+ end
448
+