doop 0.0.1
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 +15 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +203 -0
- data/Rakefile +5 -0
- data/app/assets/images/dash.gif +0 -0
- data/app/assets/images/info-icon.png +0 -0
- data/app/assets/images/progress-tick-large.png +0 -0
- data/app/helpers/doop_helper.rb +51 -0
- data/bin/.gitignore +0 -0
- data/doop.gemspec +26 -0
- data/lib/doop.rb +313 -0
- data/lib/doop/version.rb +3 -0
- data/lib/doop_controller.rb +164 -0
- data/lib/generators/doopgovuk/USAGE +10 -0
- data/lib/generators/doopgovuk/doopgovuk_generator.rb +34 -0
- data/lib/generators/doopgovuk/templates/app/assets/stylesheets/demo.css.scss +188 -0
- data/lib/generators/doopgovuk/templates/app/controllers/demo_controller.rb +155 -0
- data/lib/generators/doopgovuk/templates/app/views/demo/_preamble.html.erb +33 -0
- data/lib/generators/doopgovuk/templates/app/views/demo/_summary.html.erb +10 -0
- data/lib/generators/doopgovuk/templates/app/views/demo/_your_details.html.erb +55 -0
- data/lib/generators/doopgovuk/templates/app/views/demo/index.html.erb +3 -0
- data/lib/generators/doopgovuk/templates/app/views/demo/index.js.erb +4 -0
- data/lib/generators/doopgovuk/templates/app/views/doop/_error.html.erb +3 -0
- data/lib/generators/doopgovuk/templates/app/views/doop/_info_box.html.erb +4 -0
- data/lib/generators/doopgovuk/templates/app/views/doop/_navbar.html.erb +23 -0
- data/lib/generators/doopgovuk/templates/app/views/doop/_question.html.erb +19 -0
- data/lib/generators/doopgovuk/templates/app/views/doop/_question_form.html.erb +15 -0
- data/lib/generators/doopgovuk/templates/app/views/layouts/application.html.erb +22 -0
- data/spec/doop_spec.rb +448 -0
- data/spec/spec_helper.rb +2 -0
- 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,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" %>
|
data/spec/doop_spec.rb
ADDED
@@ -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
|
+
|