smartdown 0.7.1 → 0.8.0
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.
- data/README.md +9 -0
- data/lib/smartdown/api/flow.rb +4 -3
- data/lib/smartdown/api/node.rb +1 -1
- data/lib/smartdown/api/state.rb +10 -9
- data/lib/smartdown/engine/state.rb +2 -1
- data/lib/smartdown/engine/transition.rb +10 -10
- data/lib/smartdown/engine.rb +5 -0
- data/lib/smartdown/model/answer/base.rb +33 -5
- data/lib/smartdown/model/answer/date.rb +6 -1
- data/lib/smartdown/model/answer/money.rb +4 -1
- data/lib/smartdown/model/answer/multiple_choice.rb +11 -0
- data/lib/smartdown/model/answer/salary.rb +15 -5
- data/lib/smartdown/model/front_matter.rb +2 -2
- data/lib/smartdown/model/node.rb +17 -3
- data/lib/smartdown/parser/node_transform.rb +15 -15
- data/lib/smartdown/parser/predicates.rb +1 -1
- data/lib/smartdown/version.rb +1 -1
- data/spec/acceptance/flow_spec.rb +64 -0
- data/spec/acceptance/parsing_spec.rb +6 -0
- data/spec/api/previous_question_spec.rb +5 -1
- data/spec/api/state_spec.rb +2 -2
- data/spec/engine/interpolator_spec.rb +0 -11
- data/spec/engine/state_spec.rb +3 -2
- data/spec/engine/transition_spec.rb +1 -1
- data/spec/engine_spec.rb +79 -1
- data/spec/fixtures/acceptance/one-question/questions/q1.txt +2 -0
- data/spec/model/answer/base_spec.rb +22 -0
- data/spec/model/answer/date_spec.rb +15 -0
- data/spec/model/answer/money_spec.rb +6 -0
- data/spec/model/answer/multiple_choice_spec.rb +14 -1
- data/spec/model/answer/salary_spec.rb +29 -3
- data/spec/model/front_matter_spec.rb +35 -0
- data/spec/model/predicates/comparison_spec.rb +3 -3
- data/spec/model/predicates/function_spec.rb +11 -0
- data/spec/parser/predicates_spec.rb +15 -0
- metadata +13 -11
data/README.md
CHANGED
@@ -295,6 +295,15 @@ outcome_the_result
|
|
295
295
|
outcome_the_other_result
|
296
296
|
```
|
297
297
|
|
298
|
+
##Code terminology
|
299
|
+
|
300
|
+
####Answers vs responses
|
301
|
+
|
302
|
+
The words 'answers' and 'responses' are used for various variable names and method names throughout the gem.
|
303
|
+
Both are used to describe an answer to a question, but indicate two different formats:
|
304
|
+
* ```response``` is used for raw string inputs
|
305
|
+
* ```answer``` is used for Model::Answer objects
|
306
|
+
|
298
307
|
## Software design
|
299
308
|
|
300
309
|
The initial plan for software design can be seen in this diagram:
|
data/lib/smartdown/api/flow.rb
CHANGED
@@ -22,7 +22,8 @@ module Smartdown
|
|
22
22
|
state = smartdown_state(started, responses)
|
23
23
|
State.new(transform_node(evaluate_node(node_by_name(state.get(:current_node)), state)),
|
24
24
|
previous_question_nodes_for(state),
|
25
|
-
|
25
|
+
state.get(:accepted_responses)[1..-1] || [],
|
26
|
+
state.get(:current_answers)
|
26
27
|
)
|
27
28
|
end
|
28
29
|
|
@@ -35,11 +36,11 @@ module Smartdown
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def meta_description
|
38
|
-
front_matter.meta_description
|
39
|
+
front_matter.fetch meta_description, nil
|
39
40
|
end
|
40
41
|
|
41
42
|
def need_id
|
42
|
-
front_matter.satisfies_need
|
43
|
+
front_matter.fetch satisfies_need, nil
|
43
44
|
end
|
44
45
|
|
45
46
|
def status
|
data/lib/smartdown/api/node.rb
CHANGED
data/lib/smartdown/api/state.rb
CHANGED
@@ -4,22 +4,23 @@ module Smartdown
|
|
4
4
|
module Api
|
5
5
|
class State
|
6
6
|
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :accepted_responses, :current_node, :current_answers
|
8
8
|
|
9
|
-
def initialize(current_node, previous_questionpage_smartdown_nodes,
|
9
|
+
def initialize(current_node, previous_questionpage_smartdown_nodes, accepted_responses, current_answers)
|
10
10
|
@current_node = current_node
|
11
11
|
@previous_questionpage_smartdown_nodes = previous_questionpage_smartdown_nodes
|
12
|
-
@
|
12
|
+
@accepted_responses = accepted_responses
|
13
|
+
@current_answers = current_answers
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
previous_question_pages
|
16
|
+
def previous_answers
|
17
|
+
previous_question_pages.map { |previous_question_page|
|
17
18
|
previous_question_page.answers
|
18
19
|
}.flatten
|
19
20
|
end
|
20
21
|
|
21
|
-
def previous_question_pages
|
22
|
-
@previous_question_pages ||= build_question_pages(
|
22
|
+
def previous_question_pages
|
23
|
+
@previous_question_pages ||= build_question_pages(accepted_responses)
|
23
24
|
end
|
24
25
|
|
25
26
|
def started?
|
@@ -31,12 +32,12 @@ module Smartdown
|
|
31
32
|
end
|
32
33
|
|
33
34
|
def current_question_number
|
34
|
-
|
35
|
+
accepted_responses.count + 1
|
35
36
|
end
|
36
37
|
|
37
38
|
private
|
38
39
|
|
39
|
-
attr_reader :
|
40
|
+
attr_reader :previous_questionpage_smartdown_nodes
|
40
41
|
|
41
42
|
def build_question_pages(responses)
|
42
43
|
resp = responses.dup
|
@@ -8,7 +8,8 @@ module Smartdown
|
|
8
8
|
def initialize(data = {})
|
9
9
|
@data = duplicate_and_normalize_hash(data)
|
10
10
|
@data["path"] ||= []
|
11
|
-
@data["
|
11
|
+
@data["accepted_responses"] ||= []
|
12
|
+
@data["current_answers"] ||= []
|
12
13
|
@cached = {}
|
13
14
|
raise ArgumentError, "must specify current_node" unless has_key?("current_node")
|
14
15
|
end
|
@@ -3,12 +3,12 @@ require 'smartdown/engine/errors'
|
|
3
3
|
module Smartdown
|
4
4
|
class Engine
|
5
5
|
class Transition
|
6
|
-
attr_reader :state, :node, :
|
6
|
+
attr_reader :state, :node, :answers
|
7
7
|
|
8
|
-
def initialize(state, node,
|
8
|
+
def initialize(state, node, answers, options = {})
|
9
9
|
@state = state
|
10
10
|
@node = node
|
11
|
-
@
|
11
|
+
@answers = answers
|
12
12
|
end
|
13
13
|
|
14
14
|
def next_node
|
@@ -18,9 +18,9 @@ module Smartdown
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def next_state
|
21
|
-
|
21
|
+
state_with_responses
|
22
22
|
.put(:path, state.get(:path) + [node.name])
|
23
|
-
.put(:
|
23
|
+
.put(:accepted_responses, state.get(:accepted_responses) + answers.map(&:to_s))
|
24
24
|
.put(:current_node, next_node)
|
25
25
|
end
|
26
26
|
|
@@ -47,11 +47,11 @@ module Smartdown
|
|
47
47
|
rules.each do |rule|
|
48
48
|
case rule
|
49
49
|
when Smartdown::Model::Rule
|
50
|
-
if rule.predicate.evaluate(
|
50
|
+
if rule.predicate.evaluate(state_with_responses)
|
51
51
|
throw(:match, rule)
|
52
52
|
end
|
53
53
|
when Smartdown::Model::NestedRule
|
54
|
-
if rule.predicate.evaluate(
|
54
|
+
if rule.predicate.evaluate(state_with_responses)
|
55
55
|
throw_first_matching_rule_in(rule.children)
|
56
56
|
end
|
57
57
|
else
|
@@ -61,10 +61,10 @@ module Smartdown
|
|
61
61
|
raise Smartdown::Engine::IndeterminateNextNode
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
65
|
-
result = state.put(node.name,
|
64
|
+
def state_with_responses
|
65
|
+
result = state.put(node.name, answers.map(&:to_s))
|
66
66
|
input_variable_names_from_question.each_with_index do |input_variable_name, index|
|
67
|
-
result = result.put(input_variable_name,
|
67
|
+
result = result.put(input_variable_name, answers[index])
|
68
68
|
end
|
69
69
|
result
|
70
70
|
end
|
data/lib/smartdown/engine.rb
CHANGED
@@ -25,6 +25,7 @@ module Smartdown
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def process(raw_responses, test_start_state = nil)
|
28
|
+
#TODO: change interface to match started, raw_responses like API state...no need for shifting etc...
|
28
29
|
state = test_start_state || build_start_state
|
29
30
|
unprocessed_responses = raw_responses
|
30
31
|
while !unprocessed_responses.empty? do
|
@@ -38,6 +39,10 @@ module Smartdown
|
|
38
39
|
question.answer_type.new(unprocessed_responses.shift, question)
|
39
40
|
end
|
40
41
|
|
42
|
+
unless answers.all?(&:valid?)
|
43
|
+
state = state.put(:current_answers, answers)
|
44
|
+
break
|
45
|
+
end
|
41
46
|
transition = Transition.new(state, current_node, answers)
|
42
47
|
end
|
43
48
|
state = transition.next_state
|
@@ -5,25 +5,46 @@ module Smartdown
|
|
5
5
|
extend Forwardable
|
6
6
|
include Comparable
|
7
7
|
|
8
|
-
def_delegators :value, :to_s, :humanize, :to_i, :to_f
|
8
|
+
def_delegators :value, :to_s, :humanize, :to_i, :to_f
|
9
9
|
|
10
10
|
def value_type
|
11
11
|
::String
|
12
12
|
end
|
13
13
|
|
14
|
+
def -(other)
|
15
|
+
value - parse_other_object(other)
|
16
|
+
end
|
17
|
+
|
18
|
+
def +(other)
|
19
|
+
value + parse_other_object(other)
|
20
|
+
end
|
21
|
+
|
22
|
+
def *(other)
|
23
|
+
value * parse_other_object(other)
|
24
|
+
end
|
25
|
+
|
26
|
+
def /(other)
|
27
|
+
value / parse_other_object(other)
|
28
|
+
end
|
29
|
+
|
14
30
|
def <=>(other)
|
15
|
-
value <=>
|
31
|
+
value <=> parse_other_object(other)
|
16
32
|
end
|
17
33
|
|
18
|
-
attr_reader :question, :value
|
34
|
+
attr_reader :question, :value, :error
|
19
35
|
|
20
36
|
def initialize(value, question=nil)
|
21
|
-
@value = parse_value(value)
|
22
37
|
@question = question
|
38
|
+
@value = check_value_not_nil(value)
|
39
|
+
@value = parse_value(value) if valid?
|
40
|
+
end
|
41
|
+
|
42
|
+
def valid?
|
43
|
+
@error.nil?
|
23
44
|
end
|
24
45
|
|
25
46
|
private
|
26
|
-
def
|
47
|
+
def parse_other_object(comparison_object)
|
27
48
|
if comparison_object.is_a? Base
|
28
49
|
comparison_object.value
|
29
50
|
elsif comparison_object.is_a? value_type
|
@@ -36,6 +57,13 @@ module Smartdown
|
|
36
57
|
def parse_value(value)
|
37
58
|
value
|
38
59
|
end
|
60
|
+
|
61
|
+
def check_value_not_nil(value)
|
62
|
+
unless value
|
63
|
+
@error = "Please answer this question"
|
64
|
+
end
|
65
|
+
value
|
66
|
+
end
|
39
67
|
end
|
40
68
|
end
|
41
69
|
end
|
@@ -14,7 +14,10 @@ module Smartdown
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def humanize
|
17
|
-
"£#{'%.2f' % value}"
|
17
|
+
number_string = "£#{'%.2f' % value}".gsub(/(\d)(?=(\d\d\d)+(?!\d))/) do |digit_to_delimit|
|
18
|
+
"#{digit_to_delimit},"
|
19
|
+
end
|
20
|
+
number_string.end_with?(".00") ? number_string[0..-4] : number_string
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
@@ -11,6 +11,17 @@ module Smartdown
|
|
11
11
|
def humanize
|
12
12
|
question.choices.fetch(value)
|
13
13
|
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def parse_value(value)
|
17
|
+
check_value_not_nil(value)
|
18
|
+
if valid?
|
19
|
+
unless question.choices.keys.include? value
|
20
|
+
@error = "Invalid choice"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
value
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
16
27
|
end
|
@@ -7,23 +7,34 @@ module Smartdown
|
|
7
7
|
class Salary < Base
|
8
8
|
attr_reader :period, :amount_per_period
|
9
9
|
|
10
|
+
FORMAT_REGEX = /^£?\W*([\d|,|]+[\.]?[\d]*)[-|\W*per\W*](week|month|year)$/
|
11
|
+
|
10
12
|
def value_type
|
11
13
|
::Float
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_s
|
15
|
-
"#{'%.2f' % amount_per_period}
|
17
|
+
"#{'%.2f' % amount_per_period}-#{period}"
|
16
18
|
end
|
17
19
|
|
18
20
|
def humanize
|
19
21
|
whole, decimal = separate_by_comma(amount_per_period)
|
20
|
-
|
22
|
+
if decimal == "00"
|
23
|
+
"£#{whole} per #{period}"
|
24
|
+
else
|
25
|
+
"£#{whole}.#{decimal} per #{period}"
|
26
|
+
end
|
21
27
|
end
|
22
28
|
|
23
29
|
private
|
24
30
|
def parse_value(value)
|
25
|
-
|
26
|
-
|
31
|
+
matched_value = value.strip.match FORMAT_REGEX
|
32
|
+
unless matched_value
|
33
|
+
@error = "Invalid format"
|
34
|
+
return
|
35
|
+
end
|
36
|
+
@amount_per_period, @period = *matched_value[1..2]
|
37
|
+
@amount_per_period = @amount_per_period.gsub(",","").to_f
|
27
38
|
yearly_total
|
28
39
|
end
|
29
40
|
|
@@ -43,7 +54,6 @@ module Smartdown
|
|
43
54
|
left.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/) do |digit_to_delimit|
|
44
55
|
"#{digit_to_delimit},"
|
45
56
|
end
|
46
|
-
right = "%02d" % right.to_i
|
47
57
|
[left, right]
|
48
58
|
end
|
49
59
|
end
|
data/lib/smartdown/model/node.rb
CHANGED
@@ -12,7 +12,11 @@ module Smartdown
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def body
|
15
|
-
|
15
|
+
markdown_blocks_before_question.map { |block| as_markdown(block) }.compact.join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
def post_body
|
19
|
+
markdown_blocks_after_question.map { |block| as_markdown(block) }.compact.join("\n")
|
16
20
|
end
|
17
21
|
|
18
22
|
def questions
|
@@ -35,8 +39,18 @@ module Smartdown
|
|
35
39
|
|
36
40
|
private
|
37
41
|
|
38
|
-
def
|
39
|
-
|
42
|
+
def markdown_blocks_before_question
|
43
|
+
elements.take_while { |e|
|
44
|
+
e.is_a?(Smartdown::Model::Element::MarkdownHeading) ||
|
45
|
+
e.is_a?(Smartdown::Model::Element::MarkdownParagraph)
|
46
|
+
}[1..-1]
|
47
|
+
end
|
48
|
+
|
49
|
+
def markdown_blocks_after_question
|
50
|
+
elements.reverse.take_while { |e|
|
51
|
+
e.is_a?(Smartdown::Model::Element::MarkdownHeading) ||
|
52
|
+
e.is_a?(Smartdown::Model::Element::MarkdownParagraph)
|
53
|
+
}.reverse
|
40
54
|
end
|
41
55
|
|
42
56
|
def h1s
|
@@ -33,15 +33,15 @@ module Smartdown
|
|
33
33
|
}
|
34
34
|
|
35
35
|
rule(h1: simple(:content)) {
|
36
|
-
Smartdown::Model::Element::MarkdownHeading.new(content)
|
36
|
+
Smartdown::Model::Element::MarkdownHeading.new(content.to_s)
|
37
37
|
}
|
38
38
|
|
39
39
|
rule(p: simple(:content)) {
|
40
|
-
Smartdown::Model::Element::MarkdownParagraph.new(content)
|
40
|
+
Smartdown::Model::Element::MarkdownParagraph.new(content.to_s)
|
41
41
|
}
|
42
42
|
|
43
43
|
rule(:start_button => simple(:start_node)) {
|
44
|
-
Smartdown::Model::Element::StartButton.new(start_node)
|
44
|
+
Smartdown::Model::Element::StartButton.new(start_node.to_s)
|
45
45
|
}
|
46
46
|
|
47
47
|
rule(:front_matter => subtree(:attrs), body: subtree(:body)) {
|
@@ -66,7 +66,7 @@ module Smartdown
|
|
66
66
|
|
67
67
|
rule(:multiple_choice => {identifier: simple(:identifier), options: subtree(:choices)}) {
|
68
68
|
Smartdown::Model::Element::Question::MultipleChoice.new(
|
69
|
-
identifier, Hash[choices]
|
69
|
+
identifier.to_s, Hash[choices]
|
70
70
|
)
|
71
71
|
}
|
72
72
|
|
@@ -115,17 +115,17 @@ module Smartdown
|
|
115
115
|
}
|
116
116
|
|
117
117
|
rule(:equality_predicate => { varname: simple(:varname), expected_value: simple(:expected_value) }) {
|
118
|
-
Smartdown::Model::Predicate::Equality.new(varname, expected_value)
|
118
|
+
Smartdown::Model::Predicate::Equality.new(varname.to_s, expected_value.to_s)
|
119
119
|
}
|
120
120
|
|
121
121
|
rule(:set_value => simple(:value)) { value }
|
122
122
|
|
123
123
|
rule(:set_membership_predicate => { varname: simple(:varname), values: subtree(:values) }) {
|
124
|
-
Smartdown::Model::Predicate::SetMembership.new(varname, values)
|
124
|
+
Smartdown::Model::Predicate::SetMembership.new(varname.to_s, values)
|
125
125
|
}
|
126
126
|
|
127
127
|
rule(:named_predicate => simple(:name) ) {
|
128
|
-
Smartdown::Model::Predicate::Named.new(name)
|
128
|
+
Smartdown::Model::Predicate::Named.new(name.to_s)
|
129
129
|
}
|
130
130
|
|
131
131
|
rule(:otherwise_predicate => simple(:name) ) {
|
@@ -136,14 +136,14 @@ module Smartdown
|
|
136
136
|
Smartdown::Model::Predicate::Combined.new([first_predicate]+and_predicates)
|
137
137
|
}
|
138
138
|
|
139
|
-
rule(:function_argument => simple(:argument)) { argument }
|
139
|
+
rule(:function_argument => simple(:argument)) { argument.to_s }
|
140
140
|
|
141
141
|
rule(:function_predicate => { name: simple(:name), arguments: subtree(:arguments) }) {
|
142
|
-
Smartdown::Model::Predicate::Function.new(name, Array(arguments))
|
142
|
+
Smartdown::Model::Predicate::Function.new(name.to_s, Array(arguments))
|
143
143
|
}
|
144
144
|
|
145
145
|
rule(:function_predicate => { name: simple(:name) }) {
|
146
|
-
Smartdown::Model::Predicate::Function.new(name, [])
|
146
|
+
Smartdown::Model::Predicate::Function.new(name.to_s, [])
|
147
147
|
}
|
148
148
|
|
149
149
|
rule(:comparison_predicate => { varname: simple(:varname),
|
@@ -152,20 +152,20 @@ module Smartdown
|
|
152
152
|
}) {
|
153
153
|
case operator
|
154
154
|
when "<="
|
155
|
-
Smartdown::Model::Predicate::Comparison::LessOrEqual.new(varname, value)
|
155
|
+
Smartdown::Model::Predicate::Comparison::LessOrEqual.new(varname.to_s, value.to_s)
|
156
156
|
when "<"
|
157
|
-
Smartdown::Model::Predicate::Comparison::Less.new(varname, value)
|
157
|
+
Smartdown::Model::Predicate::Comparison::Less.new(varname.to_s, value.to_s)
|
158
158
|
when ">="
|
159
|
-
Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new(varname, value)
|
159
|
+
Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new(varname.to_s, value.to_s)
|
160
160
|
when ">"
|
161
|
-
Smartdown::Model::Predicate::Comparison::Greater.new(varname, value)
|
161
|
+
Smartdown::Model::Predicate::Comparison::Greater.new(varname.to_s, value.to_s)
|
162
162
|
else
|
163
163
|
raise "Comparison operator not recognised"
|
164
164
|
end
|
165
165
|
}
|
166
166
|
|
167
167
|
rule(:rule => {predicate: subtree(:predicate), outcome: simple(:outcome_name) } ) {
|
168
|
-
Smartdown::Model::Rule.new(predicate, outcome_name)
|
168
|
+
Smartdown::Model::Rule.new(predicate, outcome_name.to_s)
|
169
169
|
}
|
170
170
|
rule(:nested_rule => {predicate: subtree(:predicate), child_rules: subtree(:child_rules) } ) {
|
171
171
|
Smartdown::Model::NestedRule.new(predicate, child_rules)
|
data/lib/smartdown/version.rb
CHANGED
@@ -32,6 +32,26 @@ describe Smartdown::Api::Flow do
|
|
32
32
|
expect(tigers_and_cats_scenario.question_groups.first.first.name).to eq("question_1")
|
33
33
|
expect(tigers_and_cats_scenario.question_groups.first.first.answer).to eq("cat")
|
34
34
|
end
|
35
|
+
|
36
|
+
context "flow state" do
|
37
|
+
context "with no answers given" do
|
38
|
+
specify { expect(flow.state("y",[]).current_answers).to be_empty }
|
39
|
+
specify { expect(flow.state("y",[]).accepted_responses).to eq [] }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "with a valid answer given to the first question" do
|
43
|
+
specify { expect(flow.state("y",["cat"]).current_node.name).to eq "outcome_safe_pet" }
|
44
|
+
specify { expect(flow.state("y",["cat"]).accepted_responses).to eq ["cat"] }
|
45
|
+
specify { expect(flow.state("y",["cat"]).current_answers).to eq [] }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with an invalid answer given to the first question" do
|
49
|
+
specify { expect(flow.state("y",["lynx"]).current_node.name).to eq "question_1" }
|
50
|
+
specify { expect(flow.state("y",["lynx"]).accepted_responses).to eq [] }
|
51
|
+
specify { expect(flow.state("y",["lynx"]).current_answers.first.error).to eq "Invalid choice" }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
35
55
|
end
|
36
56
|
|
37
57
|
context "flow with two questions per page" do
|
@@ -60,6 +80,50 @@ describe Smartdown::Api::Flow do
|
|
60
80
|
expect(tigers_and_cats_scenario.question_groups[1][1].name).to eq("trained_for_tigers")
|
61
81
|
expect(tigers_and_cats_scenario.question_groups[1][1].answer).to eq("no")
|
62
82
|
end
|
83
|
+
|
84
|
+
context "flow state" do
|
85
|
+
context "with no answers given" do
|
86
|
+
specify { expect(flow.state("y",[]).current_answers).to be_empty }
|
87
|
+
end
|
88
|
+
|
89
|
+
context "with a valid answer given to the first question, only first answer to second page" do
|
90
|
+
specify { expect(flow.state("y",["lion", "yes", nil]).current_node.name).to eq "question_2" }
|
91
|
+
specify { expect(flow.state("y",["lion", "yes", nil]).accepted_responses).to eq ["lion"] }
|
92
|
+
specify { expect(flow.state("y",["lion", "yes", nil]).current_answers.count).to eq 2 }
|
93
|
+
specify { expect(flow.state("y",["lion", "yes", nil]).current_answers[0].valid?).to be true }
|
94
|
+
specify { expect(flow.state("y",["lion", "yes", nil]).current_answers[1].error).to eq "Please answer this question" }
|
95
|
+
end
|
96
|
+
|
97
|
+
context "with a valid answer given to the first question, only second answer to second page" do
|
98
|
+
specify { expect(flow.state("y",["lion", nil, "yes"]).current_node.name).to eq "question_2" }
|
99
|
+
specify { expect(flow.state("y",["lion", nil, "yes"]).accepted_responses).to eq ["lion"] }
|
100
|
+
specify { expect(flow.state("y",["lion", nil, "yes"]).current_answers.count).to eq 2 }
|
101
|
+
specify { expect(flow.state("y",["lion", nil, "yes"]).current_answers[1].valid?).to be true }
|
102
|
+
specify { expect(flow.state("y",["lion", nil, "yes"]).current_answers[0].error).to eq "Please answer this question" }
|
103
|
+
end
|
104
|
+
|
105
|
+
context "with a valid answer given to the first question, only first invalid answer to second page" do
|
106
|
+
specify { expect(flow.state("y",["lion", "lynx", nil]).current_node.name).to eq "question_2" }
|
107
|
+
specify { expect(flow.state("y",["lion", "lynx", nil]).accepted_responses).to eq ["lion"] }
|
108
|
+
specify { expect(flow.state("y",["lion", "lynx", nil]).current_answers.count).to eq 2 }
|
109
|
+
specify { expect(flow.state("y",["lion", "lynx", nil]).current_answers[0].error).to eq "Invalid choice" }
|
110
|
+
specify { expect(flow.state("y",["lion", "lynx", nil]).current_answers[1].error).to eq "Please answer this question" }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "with a valid answer given to the first question, empty answers to second page" do
|
114
|
+
specify { expect(flow.state("y",["lion", nil, nil]).current_node.name).to eq "question_2" }
|
115
|
+
specify { expect(flow.state("y",["lion", nil, nil]).accepted_responses).to eq ["lion"] }
|
116
|
+
specify { expect(flow.state("y",["lion", nil, nil]).current_answers.count).to eq 2 }
|
117
|
+
specify { expect(flow.state("y",["lion", nil, nil]).current_answers[0].error).to eq "Please answer this question" }
|
118
|
+
specify { expect(flow.state("y",["lion", nil, nil]).current_answers[1].error).to eq "Please answer this question" }
|
119
|
+
end
|
120
|
+
|
121
|
+
context "with valid answers to both pages" do
|
122
|
+
specify { expect(flow.state("y",["lion", "no", "no"]).current_node.name).to eq "outcome_untrained_with_lions" }
|
123
|
+
specify { expect(flow.state("y",["lion", "no", "no"]).accepted_responses).to eq ["lion", "no", "no"] }
|
124
|
+
specify { expect(flow.state("y",["lion", "no", "no"]).current_answers).to eq [] }
|
125
|
+
end
|
126
|
+
end
|
63
127
|
end
|
64
128
|
|
65
129
|
end
|
@@ -87,6 +87,12 @@ para 2.
|
|
87
87
|
EXPECTED
|
88
88
|
end
|
89
89
|
|
90
|
+
it "has a post body" do
|
91
|
+
expect(question_node.post_body).to eq(<<-EXPECTED)
|
92
|
+
Text after the question.
|
93
|
+
EXPECTED
|
94
|
+
end
|
95
|
+
|
90
96
|
it "has a multiple choice question" do
|
91
97
|
expect(question_node.questions).to match([instance_of(Smartdown::Model::Element::Question::MultipleChoice)])
|
92
98
|
end
|
@@ -5,7 +5,11 @@ describe Smartdown::Api::PreviousQuestion do
|
|
5
5
|
|
6
6
|
subject(:previous_question) { Smartdown::Api::PreviousQuestion.new(elements, response)}
|
7
7
|
let(:elements) { [ multiple_choice_element ] }
|
8
|
-
let(:multiple_choice_element) {
|
8
|
+
let(:multiple_choice_element) {
|
9
|
+
Smartdown::Model::Element::Question::MultipleChoice.new("question",
|
10
|
+
{"answer" => "Beautiful answer"}
|
11
|
+
)
|
12
|
+
}
|
9
13
|
let(:response) { double(:response) }
|
10
14
|
let(:multiple_choice_class) { double(:multiple_choice_class, :new => nil) }
|
11
15
|
let(:answer_type) { double(:answer_type) }
|
data/spec/api/state_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'smartdown/api/state'
|
|
2
2
|
|
3
3
|
describe Smartdown::Api::State do
|
4
4
|
|
5
|
-
subject(:state) { Smartdown::Api::State.new(current_node, previous_questionpage_smartdown_nodes, responses)}
|
5
|
+
subject(:state) { Smartdown::Api::State.new(current_node, previous_questionpage_smartdown_nodes, responses, double)}
|
6
6
|
let(:current_node) { double(:current_node) }
|
7
7
|
let(:previous_questionpage_smartdown_nodes) { [question_page_node_1, question_page_node_2] }
|
8
8
|
let(:question_page_node_1) { double(:question_page_node_1, :questions => [double, double]) }
|
@@ -13,7 +13,7 @@ describe Smartdown::Api::State do
|
|
13
13
|
describe "#previous_question_pages" do
|
14
14
|
it "creates question pages with their corresponding responses" do
|
15
15
|
stub_const("Smartdown::Api::PreviousQuestionPage", previous_question_page_class)
|
16
|
-
state.previous_question_pages
|
16
|
+
state.previous_question_pages
|
17
17
|
expect(Smartdown::Api::PreviousQuestionPage).to have_received(:new)
|
18
18
|
.with(question_page_node_1, ["a", "b"])
|
19
19
|
expect(Smartdown::Api::PreviousQuestionPage).to have_received(:new)
|
@@ -72,17 +72,6 @@ describe Smartdown::Engine::Interpolator do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
-
context "a paragraph containing a parslet slice" do
|
76
|
-
let(:elements) { [Smartdown::Model::Element::MarkdownParagraph.new(Parslet::Slice.new(0, 'Hello %{name}'))] }
|
77
|
-
|
78
|
-
it "interpolates without raising an error about gsub missing" do
|
79
|
-
# Note: the parser actually produces parslet 'slices' rather than strings.
|
80
|
-
# A parslet slice behaves like a string but doesn't have all of the methods of string.
|
81
|
-
# This test is to document that fact and catch any regressions.
|
82
|
-
expect(interpolated_node.elements.first.content).to eq("Hello #{example_name}")
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
75
|
context "a paragraph containing function call" do
|
87
76
|
let(:elements) { [Smartdown::Model::Element::MarkdownParagraph.new('%{double(number)}')] }
|
88
77
|
let(:state) {
|
data/spec/engine/state_spec.rb
CHANGED
@@ -10,8 +10,9 @@ describe Smartdown::Engine::State do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
it "initializes path and responses" do
|
13
|
-
expect(subject.get(:
|
13
|
+
expect(subject.get(:accepted_responses)).to eq []
|
14
14
|
expect(subject.get(:path)).to eq []
|
15
|
+
expect(subject.get(:current_answers)).to eq []
|
15
16
|
end
|
16
17
|
|
17
18
|
describe "#get" do
|
@@ -27,7 +28,7 @@ describe Smartdown::Engine::State do
|
|
27
28
|
|
28
29
|
describe "#keys" do
|
29
30
|
it "returns a set of all keys in the state" do
|
30
|
-
expect(subject.keys).to eq(Set.new(["current_node", "path", "
|
31
|
+
expect(subject.keys).to eq(Set.new(["current_node", "path", "accepted_responses", "current_answers"]))
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
@@ -88,7 +88,7 @@ describe Smartdown::Engine::Transition do
|
|
88
88
|
describe "#next_state" do
|
89
89
|
it "returns a state including a record of responses, path, and new current_node" do
|
90
90
|
expected_state = start_state
|
91
|
-
.put(:
|
91
|
+
.put(:accepted_responses, [input])
|
92
92
|
.put(:path, [current_node_name])
|
93
93
|
.put(:current_node, outcome_name1)
|
94
94
|
.put(current_node.name, input_array)
|
data/spec/engine_spec.rb
CHANGED
@@ -83,9 +83,30 @@ describe Smartdown::Engine do
|
|
83
83
|
outcome("outcome_imaginary_country")
|
84
84
|
end
|
85
85
|
rule do
|
86
|
-
set_membership_predicate("what_passport_do_you_have?", ["greek"
|
86
|
+
set_membership_predicate("what_passport_do_you_have?", ["greek"])
|
87
87
|
outcome("outcome_no_visa_needed")
|
88
88
|
end
|
89
|
+
rule do
|
90
|
+
set_membership_predicate("what_passport_do_you_have?", ["british"])
|
91
|
+
outcome("second_passport_question")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
node("second_passport_question") do
|
97
|
+
heading("What colour is your passport?")
|
98
|
+
multiple_choice(
|
99
|
+
"what_colour_is_your_passport?",
|
100
|
+
{
|
101
|
+
red: "Red",
|
102
|
+
white: "White",
|
103
|
+
blue: "Blue"
|
104
|
+
}
|
105
|
+
)
|
106
|
+
next_node_rules do
|
107
|
+
rule do
|
108
|
+
outcome("outcome_passport_colour_specified")
|
109
|
+
end
|
89
110
|
end
|
90
111
|
end
|
91
112
|
|
@@ -108,6 +129,10 @@ describe Smartdown::Engine do
|
|
108
129
|
node("outcome_with_interpolation") do
|
109
130
|
paragraph("The answer is %{interpolated_variable}")
|
110
131
|
end
|
132
|
+
|
133
|
+
node("outcome_passport_colour_specified") do
|
134
|
+
paragraph("What a pretty passport")
|
135
|
+
end
|
111
136
|
end
|
112
137
|
}
|
113
138
|
|
@@ -165,6 +190,16 @@ describe Smartdown::Engine do
|
|
165
190
|
expect { subject }.to raise_error(Smartdown::Engine::IndeterminateNextNode)
|
166
191
|
end
|
167
192
|
end
|
193
|
+
|
194
|
+
context "no passport answer entered" do
|
195
|
+
let(:responses) { ["yes", nil] }
|
196
|
+
|
197
|
+
it "raises parsing errors" do
|
198
|
+
expect(subject.get(:current_node)).to eq("passport_question")
|
199
|
+
expect(subject.get("current_answers").count).to eq 1
|
200
|
+
expect(subject.get("current_answers").first.error).to eq "Please answer this question"
|
201
|
+
end
|
202
|
+
end
|
168
203
|
end
|
169
204
|
|
170
205
|
context "two questions per page" do
|
@@ -194,6 +229,49 @@ describe Smartdown::Engine do
|
|
194
229
|
expect(subject.get("what_country_are_you_going_to?")).to eq("usa")
|
195
230
|
end
|
196
231
|
end
|
232
|
+
|
233
|
+
context "no answers given" do
|
234
|
+
let(:responses) { ["yes", nil, nil] }
|
235
|
+
it "raises parsing errors" do
|
236
|
+
expect(subject.get(:current_node)).to eq("passport_question")
|
237
|
+
expect(subject.get("current_answers").count).to eq 2
|
238
|
+
expect(subject.get("current_answers")[0].error).to eq "Please answer this question"
|
239
|
+
expect(subject.get("current_answers")[1].error).to eq "Please answer this question"
|
240
|
+
expect(subject.get("accepted_responses")).to eq ["yes"]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
context "only second answer given" do
|
245
|
+
let(:responses) { ["yes", nil, "narnia"] }
|
246
|
+
it "raises parsing errors" do
|
247
|
+
expect(subject.get(:current_node)).to eq("passport_question")
|
248
|
+
expect(subject.get("current_answers").count).to eq 2
|
249
|
+
expect(subject.get("current_answers")[0].error).to eq "Please answer this question"
|
250
|
+
expect(subject.get("current_answers")[1].error).to be nil
|
251
|
+
expect(subject.get("accepted_responses")).to eq ["yes"]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context "only first answer given" do
|
256
|
+
let(:responses) { ["yes", "greek", nil] }
|
257
|
+
it "raises parsing errors" do
|
258
|
+
expect(subject.get(:current_node)).to eq("passport_question")
|
259
|
+
expect(subject.get("current_answers").count).to eq 2
|
260
|
+
expect(subject.get("current_answers")[0].error).to be nil
|
261
|
+
expect(subject.get("current_answers")[1].error).to eq "Please answer this question"
|
262
|
+
expect(subject.get("accepted_responses")).to eq ["yes"]
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
context "british, going to usa" do
|
267
|
+
let(:responses) { ["yes", "british", "usa", nil] }
|
268
|
+
it "raises parsing errors" do
|
269
|
+
expect(subject.get(:current_node)).to eq("second_passport_question")
|
270
|
+
expect(subject.get("current_answers").count).to eq 1
|
271
|
+
expect(subject.get("current_answers").first.error).to eq "Please answer this question"
|
272
|
+
expect(subject.get("accepted_responses")).to eq ["yes", "british", "usa"]
|
273
|
+
end
|
274
|
+
end
|
197
275
|
end
|
198
276
|
end
|
199
277
|
|
@@ -20,4 +20,26 @@ describe Smartdown::Model::Answer::Base do
|
|
20
20
|
it { should respond_to(method) }
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
describe 'validations' do
|
25
|
+
describe 'valid?' do
|
26
|
+
it 'returns true if there are no errors on the question' do
|
27
|
+
expect(instance).to be_valid
|
28
|
+
end
|
29
|
+
|
30
|
+
it "has no error defined" do
|
31
|
+
expect(instance.error).to be nil
|
32
|
+
end
|
33
|
+
|
34
|
+
context "answer has been given a nil value" do
|
35
|
+
let(:value) { nil }
|
36
|
+
it 'returns false if there are no errors on the question' do
|
37
|
+
expect(instance).not_to be_valid
|
38
|
+
end
|
39
|
+
it "has an error asking for user to input a value" do
|
40
|
+
expect(instance.error).to eq "Please answer this question"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
23
45
|
end
|
@@ -14,6 +14,21 @@ describe Smartdown::Model::Answer::Date do
|
|
14
14
|
specify { expect(instance.humanize).to eql "10 January 2000" }
|
15
15
|
end
|
16
16
|
|
17
|
+
describe "errors" do
|
18
|
+
|
19
|
+
context "format is incorrect" do
|
20
|
+
let(:date_string) { "tomorrow" }
|
21
|
+
specify { expect(instance.error).to eql "Invalid date" }
|
22
|
+
specify { expect(instance.value).to eql nil }
|
23
|
+
end
|
24
|
+
|
25
|
+
context "answer not filled in" do
|
26
|
+
let(:date_string) { nil }
|
27
|
+
specify { expect(instance.error).to eql "Please answer this question" }
|
28
|
+
specify { expect(instance.value).to eql nil }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
17
32
|
describe "comparisons" do
|
18
33
|
let(:date_string) { "2000-1-10" }
|
19
34
|
|
@@ -28,5 +28,11 @@ describe Smartdown::Model::Answer::Money do
|
|
28
28
|
expect(instance.humanize).to eql("£523.42")
|
29
29
|
end
|
30
30
|
end
|
31
|
+
context "no pence" do
|
32
|
+
let(:money_float) { 523.00 }
|
33
|
+
it "rounds down amounts of money correctly" do
|
34
|
+
expect(instance.humanize).to eql("£523")
|
35
|
+
end
|
36
|
+
end
|
31
37
|
end
|
32
38
|
end
|
@@ -3,7 +3,8 @@ require 'smartdown/model/answer/multiple_choice'
|
|
3
3
|
|
4
4
|
describe Smartdown::Model::Answer::MultipleChoice do
|
5
5
|
|
6
|
-
|
6
|
+
let(:answer_string) { "answer" }
|
7
|
+
subject(:answer) { described_class.new(answer_string, multiple_choice_question) }
|
7
8
|
let(:multiple_choice_question) {
|
8
9
|
Smartdown::Model::Element::Question::MultipleChoice.new("question",
|
9
10
|
{"answer" => "Beautiful answer"}
|
@@ -15,4 +16,16 @@ describe Smartdown::Model::Answer::MultipleChoice do
|
|
15
16
|
expect(answer.humanize).to eq "Beautiful answer"
|
16
17
|
end
|
17
18
|
end
|
19
|
+
|
20
|
+
describe "errors" do
|
21
|
+
context "format is incorrect" do
|
22
|
+
let(:answer_string) { "kasjdf" }
|
23
|
+
specify { expect(answer.error).to eql "Invalid choice" }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "answer not filled in" do
|
27
|
+
let(:answer_string) { nil }
|
28
|
+
specify { expect(answer.error).to eql "Please answer this question" }
|
29
|
+
end
|
30
|
+
end
|
18
31
|
end
|
@@ -11,18 +11,18 @@ describe Smartdown::Model::Answer::Salary do
|
|
11
11
|
specify { expect(instance.amount_per_period).to eql(500.00) }
|
12
12
|
|
13
13
|
it "as a string, it should declare itself in the initial format provided" do
|
14
|
-
expect(instance.to_s).to eql("500.00
|
14
|
+
expect(instance.to_s).to eql("500.00-week")
|
15
15
|
end
|
16
16
|
|
17
17
|
describe "#humanize" do
|
18
18
|
it "declares itself in the initial format provided" do
|
19
|
-
expect(instance.humanize).to eql("£500
|
19
|
+
expect(instance.humanize).to eql("£500 per week")
|
20
20
|
end
|
21
21
|
|
22
22
|
context "amounts over 999" do
|
23
23
|
let(:salary_string) { "15000-week" }
|
24
24
|
it "adds commas" do
|
25
|
-
expect(instance.humanize).to eql("£15,000
|
25
|
+
expect(instance.humanize).to eql("£15,000 per week")
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
@@ -46,6 +46,32 @@ describe Smartdown::Model::Answer::Salary do
|
|
46
46
|
expect(instance.humanize).to eql("£15,000.56 per week")
|
47
47
|
end
|
48
48
|
end
|
49
|
+
|
50
|
+
context "amounts with commas" do
|
51
|
+
let(:salary_string) { "15,0000-week" }
|
52
|
+
it "correct comma location" do
|
53
|
+
expect(instance.humanize).to eql("£150,000 per week")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "errors" do
|
59
|
+
context "invalid formatting" do
|
60
|
+
let(:salary_string) {"Loads'a'money"}
|
61
|
+
|
62
|
+
it "Has errors" do
|
63
|
+
expect(instance.error).to eql("Invalid format")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "no input" do
|
68
|
+
let(:salary_string) { nil }
|
69
|
+
|
70
|
+
it "Has errors" do
|
71
|
+
expect(instance.error).to eql("Please answer this question")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
49
75
|
end
|
50
76
|
|
51
77
|
context "declared by week" do
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'smartdown/model/front_matter.rb'
|
2
|
+
|
3
|
+
describe Smartdown::Model::FrontMatter do
|
4
|
+
|
5
|
+
subject { described_class.new({ 'key' => :thing }) }
|
6
|
+
|
7
|
+
describe "fetch" do
|
8
|
+
context 'Has the attribute desired' do
|
9
|
+
it { expect(subject.fetch 'key').to eq(:thing) }
|
10
|
+
|
11
|
+
it "doesn't use the default" do
|
12
|
+
expect(subject.fetch 'key', 'default').to eq(:thing)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context 'Does not have the attribute desired' do
|
17
|
+
it 'raises error by default if the attribute is missing' do
|
18
|
+
expect { subject.fetch :hahhad }.to raise_error
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'allows passing a default to return on failure' do
|
22
|
+
expect(subject.fetch :askdjf, 'draft').to eq ('draft')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'allows passing a nil default to return on failure' do
|
26
|
+
expect(subject.fetch :askdjf, nil).to be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'raises an error if more than 2 arguments are passed' do
|
30
|
+
expect { subject.fetch :askdjf, 'draft', 'x' }.to raise_error(ArgumentError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -114,7 +114,7 @@ describe "comparison predicates" do
|
|
114
114
|
end
|
115
115
|
|
116
116
|
context "state has lower value" do
|
117
|
-
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-1-30") }
|
117
|
+
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: Smartdown::Model::Answer::Date.new("2014-1-30")) }
|
118
118
|
let(:results) { {
|
119
119
|
:greater => false,
|
120
120
|
:greater_or_equal => false,
|
@@ -129,7 +129,7 @@ describe "comparison predicates" do
|
|
129
129
|
end
|
130
130
|
|
131
131
|
context "state has identical value" do
|
132
|
-
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-1-31") }
|
132
|
+
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: Smartdown::Model::Answer::Date.new("2014-1-31")) }
|
133
133
|
let(:results) { {
|
134
134
|
:greater => false,
|
135
135
|
:greater_or_equal => true,
|
@@ -144,7 +144,7 @@ describe "comparison predicates" do
|
|
144
144
|
end
|
145
145
|
|
146
146
|
context "state has higher value" do
|
147
|
-
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-2-1") }
|
147
|
+
let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: Smartdown::Model::Answer::Date.new("2014-2-1")) }
|
148
148
|
let(:results) { {
|
149
149
|
:greater => true,
|
150
150
|
:greater_or_equal => true,
|
@@ -45,6 +45,17 @@ describe Smartdown::Model::Predicate::Function do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
context "with a ? in the function name" do
|
49
|
+
let(:function_name) { "is_odd?" }
|
50
|
+
let(:my_function) { ->(number) { number.odd? } }
|
51
|
+
subject(:predicate) { described_class.new(function_name, ["number"]) }
|
52
|
+
let(:state) { Smartdown::Engine::State.new(current_node: "n", "number" => 3, function_name => my_function) }
|
53
|
+
|
54
|
+
it "can be called normally" do
|
55
|
+
expect(predicate.evaluate(state)).to eq(true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
48
59
|
context "with nested functions" do
|
49
60
|
# nesting looks like: function_1(function_2(5))
|
50
61
|
let(:function_1) { ->(x) { x - 1 } }
|
@@ -140,6 +140,21 @@ describe Smartdown::Parser::Predicates do
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
+
context "with ? in name" do
|
144
|
+
let(:source) { "function_name?()" }
|
145
|
+
|
146
|
+
it { should parse(source).as(function_predicate: { name: "function_name?" }) }
|
147
|
+
|
148
|
+
describe "transformed" do
|
149
|
+
let(:node_name) { "my_node" }
|
150
|
+
subject(:transformed) {
|
151
|
+
Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
|
152
|
+
}
|
153
|
+
|
154
|
+
it { should eq(Smartdown::Model::Predicate::Function.new("function_name?", [])) }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
143
158
|
context "single argument" do
|
144
159
|
let(:source) { "function_name(arg_1)" }
|
145
160
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smartdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2014-07-09 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
16
|
-
requirement: &
|
16
|
+
requirement: &20602040 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.6.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *20602040
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &20601400 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.0.0
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *20601400
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rake
|
38
|
-
requirement: &
|
38
|
+
requirement: &20600760 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *20600760
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: gem_publisher
|
49
|
-
requirement: &
|
49
|
+
requirement: &20600200 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,7 +54,7 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *20600200
|
58
58
|
description:
|
59
59
|
email: david.heath@digital.cabinet-office.gov.uk
|
60
60
|
executables:
|
@@ -213,6 +213,7 @@ files:
|
|
213
213
|
- spec/support_specs/model_builder_spec.rb
|
214
214
|
- spec/engine_spec.rb
|
215
215
|
- spec/model/node_spec.rb
|
216
|
+
- spec/model/front_matter_spec.rb
|
216
217
|
- spec/model/flow_spec.rb
|
217
218
|
- spec/model/answer/money_spec.rb
|
218
219
|
- spec/model/answer/multiple_choice_spec.rb
|
@@ -241,7 +242,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
241
242
|
version: '0'
|
242
243
|
segments:
|
243
244
|
- 0
|
244
|
-
hash:
|
245
|
+
hash: -4304436723939506743
|
245
246
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
246
247
|
none: false
|
247
248
|
requirements:
|
@@ -250,7 +251,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
251
|
version: '0'
|
251
252
|
segments:
|
252
253
|
- 0
|
253
|
-
hash:
|
254
|
+
hash: -4304436723939506743
|
254
255
|
requirements: []
|
255
256
|
rubyforge_project:
|
256
257
|
rubygems_version: 1.8.11
|
@@ -327,6 +328,7 @@ test_files:
|
|
327
328
|
- spec/support_specs/model_builder_spec.rb
|
328
329
|
- spec/engine_spec.rb
|
329
330
|
- spec/model/node_spec.rb
|
331
|
+
- spec/model/front_matter_spec.rb
|
330
332
|
- spec/model/flow_spec.rb
|
331
333
|
- spec/model/answer/money_spec.rb
|
332
334
|
- spec/model/answer/multiple_choice_spec.rb
|