smartdown 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -132,7 +132,7 @@ passport_country:
132
132
  [country: passport_country]
133
133
  ```
134
134
 
135
- ### Date (tbd)
135
+ ### Date
136
136
 
137
137
  ```markdown
138
138
  ## What is the baby’s due date?
@@ -140,8 +140,6 @@ passport_country:
140
140
  [date: baby_due_date]
141
141
  ```
142
142
 
143
- Asks for a specific date in the given range. Ranges can be expressed as a relative number of years or absolute dates in YYYY-MM-DD format.
144
-
145
143
  ### Value (tbd)
146
144
 
147
145
  ```markdown
@@ -158,14 +156,12 @@ Asks for an arbitrary text input.
158
156
 
159
157
  Asks for a numerical input which can have decimals and optional thousand-separating commas.
160
158
 
161
- ### Salary (tbd)
159
+ ### Salary
162
160
 
163
161
  ```markdown
164
162
  [salary: salary_value]
165
163
  ```
166
164
 
167
- Asks for salary which can be expressed as either a weekly or monthly money amount. The user chooses between weekly/monthly
168
-
169
165
  ### Checkbox (tbd)
170
166
 
171
167
  ```markdown
@@ -0,0 +1,8 @@
1
+ require 'smartdown/api/question'
2
+
3
+ module Smartdown
4
+ module Api
5
+ class DateQuestion < Question
6
+ end
7
+ end
8
+ end
@@ -69,6 +69,10 @@ module Smartdown
69
69
  nodes.select{ |node| node.is_a? Smartdown::Api::Outcome}
70
70
  end
71
71
 
72
+ def coversheet
73
+ @coversheet ||= Smartdown::Api::Coversheet.new(@smartdown_flow.coversheet)
74
+ end
75
+
72
76
  private
73
77
 
74
78
  def transform_node(node)
@@ -85,10 +89,6 @@ module Smartdown
85
89
  end
86
90
  end
87
91
 
88
- def coversheet
89
- @coversheet ||= Smartdown::Api::Coversheet.new(@smartdown_flow.coversheet)
90
- end
91
-
92
92
  def front_matter
93
93
  @front_matter ||= coversheet.front_matter
94
94
  end
@@ -9,12 +9,12 @@ module Smartdown
9
9
  headings = node_elements.select { |element|
10
10
  element.is_a? Smartdown::Model::Element::MarkdownHeading
11
11
  }
12
- @title = headings.first.content.to_s if headings.first
13
12
  nb_questions = node_elements.select{ |element|
14
- element.is_a? Smartdown::Model::Element::Question::MultipleChoice
13
+ element.class.to_s.include?("Smartdown::Model::Element::Question")
15
14
  }.count
16
15
  if headings.count > nb_questions
17
16
  node_elements.delete(headings.first) #Remove page title
17
+ @title = headings.first.content.to_s
18
18
  end
19
19
  @elements = node_elements
20
20
  @front_matter = node.front_matter
@@ -5,12 +5,16 @@ module Smartdown
5
5
 
6
6
  def_delegators :@question, :title, :options
7
7
 
8
- attr_reader :response
8
+ attr_reader :response, :question
9
9
 
10
10
  def initialize(elements, response)
11
11
  @response = response
12
12
  if elements.find{|element| element.is_a? Smartdown::Model::Element::Question::MultipleChoice}
13
13
  @question = MultipleChoice.new(elements)
14
+ elsif elements.find{|element| element.is_a? Smartdown::Model::Element::Question::Date}
15
+ @question = DateQuestion.new(elements)
16
+ elsif elements.find{|element| element.is_a? Smartdown::Model::Element::Question::Salary}
17
+ @question = SalaryQuestion.new(elements)
14
18
  end
15
19
  end
16
20
 
@@ -13,7 +13,7 @@ module Smartdown
13
13
  }
14
14
  @title = headings.first.content.to_s if headings.first
15
15
  nb_questions = node_elements.select{ |element|
16
- element.is_a? Smartdown::Model::Element::Question::MultipleChoice
16
+ element.class.to_s.include?("Smartdown::Model::Element::Question")
17
17
  }.count
18
18
  if headings.count > nb_questions
19
19
  node_elements.delete(headings.first) #Remove page title
@@ -1,4 +1,6 @@
1
1
  require 'smartdown/api/multiple_choice'
2
+ require 'smartdown/api/date_question'
3
+ require 'smartdown/api/salary_question'
2
4
 
3
5
  module Smartdown
4
6
  module Api
@@ -9,6 +11,10 @@ module Smartdown
9
11
  end.map do |question_element_group|
10
12
  if question_element_group.find{|element| element.is_a? Smartdown::Model::Element::Question::MultipleChoice}
11
13
  Smartdown::Api::MultipleChoice.new(question_element_group)
14
+ elsif question_element_group.find{|element| element.is_a? Smartdown::Model::Element::Question::Date}
15
+ Smartdown::Api::DateQuestion.new(question_element_group)
16
+ elsif question_element_group.find{|element| element.is_a? Smartdown::Model::Element::Question::Salary}
17
+ Smartdown::Api::SalaryQuestion.new(question_element_group)
12
18
  end
13
19
  end
14
20
  end
@@ -0,0 +1,8 @@
1
+ require 'smartdown/api/question'
2
+
3
+ module Smartdown
4
+ module Api
5
+ class SalaryQuestion < Question
6
+ end
7
+ end
8
+ end
@@ -14,6 +14,10 @@ module Smartdown
14
14
  ->(state) { predicate.values.include?(state.get(predicate.varname)) }
15
15
  when Smartdown::Model::Predicate::Named
16
16
  ->(state) { state.get(predicate.name) }
17
+ when Smartdown::Model::Predicate::Comparison::Base
18
+ ->(state) { predicate.evaluate(state.get(predicate.varname)) }
19
+ when Smartdown::Model::Predicate::Combined
20
+ ->(state) { predicate.predicates.map { |p| evaluate(p, state) }.all? }
17
21
  else
18
22
  raise "Unknown predicate type #{predicate.class}"
19
23
  end
@@ -0,0 +1,9 @@
1
+ module Smartdown
2
+ module Model
3
+ module Element
4
+ module Question
5
+ Date = Struct.new(:name)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Smartdown
2
+ module Model
3
+ module Element
4
+ module Question
5
+ Salary = Struct.new(:name)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Smartdown
2
+ module Model
3
+ module Predicate
4
+ Combined = Struct.new(:predicates)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Smartdown
2
+ module Model
3
+ module Predicate
4
+ module Comparison
5
+ Base = Struct.new(:varname, :value)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ require 'smartdown/model/predicate/comparison/base'
2
+ require 'date'
3
+
4
+ module Smartdown
5
+ module Model
6
+ module Predicate
7
+ module Comparison
8
+ class Greater < Base
9
+ def evaluate(variable)
10
+ if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
+ Date.parse(variable) > Date.parse(value)
12
+ else
13
+ variable > value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'smartdown/model/predicate/comparison/base'
2
+ require 'date'
3
+
4
+ module Smartdown
5
+ module Model
6
+ module Predicate
7
+ module Comparison
8
+ class GreaterOrEqual < Base
9
+ def evaluate(variable)
10
+ if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
+ Date.parse(variable) >= Date.parse(value)
12
+ else
13
+ variable >= value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'smartdown/model/predicate/comparison/base'
2
+ require 'date'
3
+
4
+ module Smartdown
5
+ module Model
6
+ module Predicate
7
+ module Comparison
8
+ class Less < Base
9
+ def evaluate(variable)
10
+ if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
+ Date.parse(variable) < Date.parse(value)
12
+ else
13
+ variable < value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'smartdown/model/predicate/comparison/base'
2
+ require 'date'
3
+
4
+ module Smartdown
5
+ module Model
6
+ module Predicate
7
+ module Comparison
8
+ class LessOrEqual < Base
9
+ def evaluate(variable)
10
+ if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
+ Date.parse(variable) <= Date.parse(value)
12
+ else
13
+ variable <= value
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'smartdown/parser/base'
2
+
3
+ module Smartdown
4
+ module Parser
5
+ module Element
6
+ class DateQuestion < Base
7
+ rule(:date_question) {
8
+ (
9
+ str("[date:") >>
10
+ optional_space >>
11
+ question_identifier.as(:identifier) >>
12
+ optional_space >>
13
+ str("]") >>
14
+ optional_space >>
15
+ line_ending
16
+ ).as(:date)
17
+ }
18
+ root(:date_question)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'smartdown/parser/base'
2
+
3
+ module Smartdown
4
+ module Parser
5
+ module Element
6
+ class SalaryQuestion < Base
7
+ rule(:salary_question) {
8
+ (
9
+ str("[salary:") >>
10
+ optional_space >>
11
+ question_identifier.as(:identifier) >>
12
+ optional_space >>
13
+ str("]") >>
14
+ optional_space >>
15
+ line_ending
16
+ ).as(:salary)
17
+ }
18
+ root(:salary_question)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -3,6 +3,8 @@ require 'smartdown/parser/rules'
3
3
  require 'smartdown/parser/element/front_matter'
4
4
  require 'smartdown/parser/element/start_button'
5
5
  require 'smartdown/parser/element/multiple_choice_question'
6
+ require 'smartdown/parser/element/date_question'
7
+ require 'smartdown/parser/element/salary_question'
6
8
  require 'smartdown/parser/element/markdown_heading'
7
9
  require 'smartdown/parser/element/markdown_paragraph'
8
10
  require 'smartdown/parser/element/conditional'
@@ -15,6 +17,8 @@ module Smartdown
15
17
  Element::Conditional.new |
16
18
  Element::MarkdownHeading.new |
17
19
  Element::MultipleChoiceQuestion.new |
20
+ Element::DateQuestion.new |
21
+ Element::SalaryQuestion.new |
18
22
  Rules.new |
19
23
  Element::StartButton.new |
20
24
  Element::NextSteps.new |
@@ -5,6 +5,8 @@ require 'smartdown/model/rule'
5
5
  require 'smartdown/model/nested_rule'
6
6
  require 'smartdown/model/next_node_rules'
7
7
  require 'smartdown/model/element/question/multiple_choice'
8
+ require 'smartdown/model/element/question/date'
9
+ require 'smartdown/model/element/question/salary'
8
10
  require 'smartdown/model/element/start_button'
9
11
  require 'smartdown/model/element/markdown_heading'
10
12
  require 'smartdown/model/element/markdown_paragraph'
@@ -13,6 +15,11 @@ require 'smartdown/model/element/next_steps'
13
15
  require 'smartdown/model/predicate/equality'
14
16
  require 'smartdown/model/predicate/set_membership'
15
17
  require 'smartdown/model/predicate/named'
18
+ require 'smartdown/model/predicate/combined'
19
+ require 'smartdown/model/predicate/comparison/greater_or_equal'
20
+ require 'smartdown/model/predicate/comparison/greater'
21
+ require 'smartdown/model/predicate/comparison/less_or_equal'
22
+ require 'smartdown/model/predicate/comparison/less'
16
23
 
17
24
  module Smartdown
18
25
  module Parser
@@ -61,6 +68,18 @@ module Smartdown
61
68
  )
62
69
  }
63
70
 
71
+ rule(:date => {identifier: simple(:identifier)}) {
72
+ Smartdown::Model::Element::Question::Date.new(
73
+ identifier.to_s
74
+ )
75
+ }
76
+
77
+ rule(:salary => {identifier: simple(:identifier)}) {
78
+ Smartdown::Model::Element::Question::Salary.new(
79
+ identifier.to_s
80
+ )
81
+ }
82
+
64
83
  rule(:next_steps => { content: simple(:content) }) {
65
84
  Smartdown::Model::Element::NextSteps.new(content.to_s)
66
85
  }
@@ -92,6 +111,7 @@ module Smartdown
92
111
  }
93
112
 
94
113
  rule(:set_value => simple(:value)) { value }
114
+
95
115
  rule(:set_membership_predicate => { varname: simple(:varname), values: subtree(:values) }) {
96
116
  Smartdown::Model::Predicate::SetMembership.new(varname, values)
97
117
  }
@@ -100,6 +120,29 @@ module Smartdown
100
120
  Smartdown::Model::Predicate::Named.new(name)
101
121
  }
102
122
 
123
+
124
+ rule(:combined_predicate => {first_predicate: subtree(:first_predicate), and_predicates: subtree(:and_predicates) }) {
125
+ Smartdown::Model::Predicate::Combined.new([first_predicate]+and_predicates)
126
+ }
127
+
128
+ rule(:comparison_predicate => { varname: simple(:varname),
129
+ value: simple(:value),
130
+ operator: simple(:operator)
131
+ }) {
132
+ case operator
133
+ when "<="
134
+ Smartdown::Model::Predicate::Comparison::LessOrEqual.new(varname, value)
135
+ when "<"
136
+ Smartdown::Model::Predicate::Comparison::Less.new(varname, value)
137
+ when ">="
138
+ Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new(varname, value)
139
+ when ">"
140
+ Smartdown::Model::Predicate::Comparison::Greater.new(varname, value)
141
+ else
142
+ raise "Comparison operator not recognised"
143
+ end
144
+ }
145
+
103
146
  rule(:rule => {predicate: subtree(:predicate), outcome: simple(:outcome_name) } ) {
104
147
  Smartdown::Model::Rule.new(predicate, outcome_name)
105
148
  }
@@ -9,6 +9,16 @@ module Smartdown
9
9
  str("'") >> match("[^']").repeat.as(:expected_value) >> str("'")
10
10
  }
11
11
 
12
+ rule(:comparison_operator) {
13
+ str('>=') | str('>') | str('<=') | str('<')
14
+ }
15
+
16
+ rule(:comparison_predicate) {
17
+ identifier.as(:varname) >> some_space >>
18
+ comparison_operator.as(:operator) >> some_space >>
19
+ str("'") >> match("[^']").repeat.as(:value) >> str("'")
20
+ }
21
+
12
22
  rule(:set_value) {
13
23
  match('[^\s}]').repeat(1).as(:set_value)
14
24
  }
@@ -26,8 +36,23 @@ module Smartdown
26
36
  rule(:named_predicate) {
27
37
  question_identifier.as(:named_predicate)
28
38
  }
29
- rule(:predicates) {
30
- equality_predicate.as(:equality_predicate) | set_membership_predicate.as(:set_membership_predicate) | named_predicate
39
+
40
+ rule(:predicate) {
41
+ equality_predicate.as(:equality_predicate) |
42
+ set_membership_predicate.as(:set_membership_predicate) |
43
+ comparison_predicate.as(:comparison_predicate) |
44
+ named_predicate
45
+ }
46
+
47
+ rule (:combined_predicate) {
48
+ predicate.as(:first_predicate) >>
49
+ (some_space >> str('AND') >> some_space >>
50
+ predicate).repeat(1).as(:and_predicates)
51
+ }
52
+
53
+ rule (:predicates) {
54
+ combined_predicate.as(:combined_predicate) |
55
+ predicate
31
56
  }
32
57
 
33
58
  root(:predicates)
@@ -1,3 +1,3 @@
1
1
  module Smartdown
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
@@ -2,10 +2,15 @@ require 'smartdown/engine/predicate_evaluator'
2
2
  require 'smartdown/model/predicate/equality'
3
3
  require 'smartdown/model/predicate/set_membership'
4
4
  require 'smartdown/model/predicate/named'
5
+ require 'smartdown/model/predicate/combined'
6
+ require 'smartdown/model/predicate/comparison/greater_or_equal'
7
+ require 'smartdown/model/predicate/comparison/greater'
8
+ require 'smartdown/model/predicate/comparison/less_or_equal'
9
+ require 'smartdown/model/predicate/comparison/less'
5
10
  require 'smartdown/engine/state'
6
11
 
7
12
  describe Smartdown::Engine::PredicateEvaluator do
8
- subject(:evalutator) { described_class.new }
13
+ subject(:evaluator) { described_class.new }
9
14
 
10
15
  context "equality predicate" do
11
16
  let(:predicate) { Smartdown::Model::Predicate::Equality.new("my_var", "some value") }
@@ -15,23 +20,23 @@ describe Smartdown::Engine::PredicateEvaluator do
15
20
  let(:state) { Smartdown::Engine::State.new(current_node: "n") }
16
21
 
17
22
  it "raises an UndefinedValue error" do
18
- expect { evalutator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
23
+ expect { evaluator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
19
24
  end
20
25
  end
21
26
 
22
27
  context "state has expected variable with same value" do
23
28
  let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "some value") }
24
29
 
25
- it "evalutes to true" do
26
- expect(evalutator.evaluate(predicate, state)).to eq(true)
30
+ it "evaluates to true" do
31
+ expect(evaluator.evaluate(predicate, state)).to eq(true)
27
32
  end
28
33
  end
29
34
 
30
35
  context "state has expected variable with a different value" do
31
36
  let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "some other value") }
32
37
 
33
- it "evalutes to false" do
34
- expect(evalutator.evaluate(predicate, state)).to eq(false)
38
+ it "evaluates to false" do
39
+ expect(evaluator.evaluate(predicate, state)).to eq(false)
35
40
  end
36
41
  end
37
42
  end
@@ -47,15 +52,15 @@ describe Smartdown::Engine::PredicateEvaluator do
47
52
  let(:state) { Smartdown::Engine::State.new(current_node: "n") }
48
53
 
49
54
  it "raises an UndefinedValue error" do
50
- expect { evalutator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
55
+ expect { evaluator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
51
56
  end
52
57
  end
53
58
 
54
59
  context "state has expected variable with one of the expected values" do
55
- it "evalutes to true" do
60
+ it "evaluates to true" do
56
61
  expected_values.each do |value|
57
62
  state = Smartdown::Engine::State.new(current_node: "n", varname => value)
58
- expect(evalutator.evaluate(predicate, state)).to eq(true)
63
+ expect(evaluator.evaluate(predicate, state)).to eq(true)
59
64
  end
60
65
  end
61
66
  end
@@ -63,8 +68,8 @@ describe Smartdown::Engine::PredicateEvaluator do
63
68
  context "state has expected variable with a value not in the list of expected values" do
64
69
  let(:state) { Smartdown::Engine::State.new(current_node: "n", varname => "some other value") }
65
70
 
66
- it "evalutes to false" do
67
- expect(evalutator.evaluate(predicate, state)).to eq(false)
71
+ it "evaluates to false" do
72
+ expect(evaluator.evaluate(predicate, state)).to eq(false)
68
73
  end
69
74
  end
70
75
  end
@@ -79,7 +84,7 @@ describe Smartdown::Engine::PredicateEvaluator do
79
84
  let(:state) { Smartdown::Engine::State.new(current_node: "n") }
80
85
 
81
86
  it "raises an UndefinedValue error" do
82
- expect { evalutator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
87
+ expect { evaluator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
83
88
  end
84
89
  end
85
90
 
@@ -89,10 +94,191 @@ describe Smartdown::Engine::PredicateEvaluator do
89
94
  }
90
95
 
91
96
  it "fetches the predicate value from the state" do
92
- expect(evalutator.evaluate(predicate, state)).to eq(true)
97
+ expect(evaluator.evaluate(predicate, state)).to eq(true)
93
98
  end
94
99
  end
95
100
  end
96
101
  end
97
102
 
103
+ context "combined predicate" do
104
+ let(:predicate_name) { "my_pred?" }
105
+ let(:predicate_name_2) { "my_other_pred?" }
106
+ let(:predicate) { Smartdown::Model::Predicate::Named.new(predicate_name) }
107
+ let(:predicate_2) { Smartdown::Model::Predicate::Named.new(predicate_name_2) }
108
+ let(:combined_predicate) { Smartdown::Model::Predicate::Combined.new([predicate, predicate_2])}
109
+
110
+ describe "#evaluate" do
111
+
112
+ context "both states are true" do
113
+ let(:state) {
114
+ Smartdown::Engine::State.new("current_node" => "n", "my_pred?" => true, "my_other_pred?" => true )
115
+ }
116
+
117
+ it "evaluates as true" do
118
+ expect(evaluator.evaluate(combined_predicate, state)).to eq(true)
119
+ end
120
+ end
121
+
122
+ context "both states are false" do
123
+ let(:state) {
124
+ Smartdown::Engine::State.new("current_node" => "n", "my_pred?" => false, "my_other_pred?" => false )
125
+ }
126
+
127
+ it "evaluates as false" do
128
+ expect(evaluator.evaluate(combined_predicate, state)).to eq(false)
129
+ end
130
+ end
131
+
132
+ context "one of the states is false" do
133
+ let(:state) {
134
+ Smartdown::Engine::State.new("current_node" => "n", "my_pred?" => true, "my_other_pred?" => false )
135
+ }
136
+
137
+ it "evaluates as false" do
138
+ expect(evaluator.evaluate(combined_predicate, state)).to eq(false)
139
+ end
140
+ end
141
+ end
142
+
143
+ end
144
+
145
+ context "comparison predicates with integers" do
146
+ let(:greater_predicate) { Smartdown::Model::Predicate::Comparison::Greater.new("my_var", "5") }
147
+ let(:greater_or_equal_predicate) { Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new("my_var", "5") }
148
+ let(:less_predicate) { Smartdown::Model::Predicate::Comparison::Less.new("my_var", "5") }
149
+ let(:less_or_equal_predicate) { Smartdown::Model::Predicate::Comparison::LessOrEqual.new("my_var", "5") }
150
+ let(:predicates) { {
151
+ :greater => greater_predicate,
152
+ :greater_or_equal => greater_or_equal_predicate,
153
+ :less => less_predicate,
154
+ :less_or_equal => less_or_equal_predicate
155
+ } }
156
+
157
+ describe "#evaluate" do
158
+ context "state missing expected variable" do
159
+ let(:state) { Smartdown::Engine::State.new(current_node: "n") }
160
+
161
+ it "raises an UndefinedValue error" do
162
+ predicates.values.each do |predicate|
163
+ expect { evaluator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
164
+ end
165
+ end
166
+ end
167
+
168
+ context "state has lower value" do
169
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "4") }
170
+ let(:results) { {
171
+ :greater => false,
172
+ :greater_or_equal => false,
173
+ :less => true,
174
+ :less_or_equal => true
175
+ } }
176
+ it "evaluates correctly" do
177
+ predicates.each do |predicate_key, predicate|
178
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
179
+ end
180
+ end
181
+ end
182
+
183
+ context "state has identical value" do
184
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "5") }
185
+ let(:results) { {
186
+ :greater => false,
187
+ :greater_or_equal => true,
188
+ :less => false,
189
+ :less_or_equal => true
190
+ } }
191
+ it "evaluates correctly" do
192
+ predicates.each do |predicate_key, predicate|
193
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
194
+ end
195
+ end
196
+ end
197
+
198
+ context "state has higher value" do
199
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "6") }
200
+ let(:results) { {
201
+ :greater => true,
202
+ :greater_or_equal => true,
203
+ :less => false,
204
+ :less_or_equal => false
205
+ } }
206
+ it "evaluates correctly" do
207
+ predicates.each do |predicate_key, predicate|
208
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+
215
+ context "comparison predicates with dates" do
216
+ let(:greater_predicate) { Smartdown::Model::Predicate::Comparison::Greater.new("my_var", "2014-1-31") }
217
+ let(:greater_or_equal_predicate) { Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new("my_var", "2014-1-31") }
218
+ let(:less_predicate) { Smartdown::Model::Predicate::Comparison::Less.new("my_var", "2014-1-31") }
219
+ let(:less_or_equal_predicate) { Smartdown::Model::Predicate::Comparison::LessOrEqual.new("my_var", "2014-1-31") }
220
+ let(:predicates) { {
221
+ :greater => greater_predicate,
222
+ :greater_or_equal => greater_or_equal_predicate,
223
+ :less => less_predicate,
224
+ :less_or_equal => less_or_equal_predicate
225
+ } }
226
+
227
+ describe "#evaluate" do
228
+ context "state missing expected variable" do
229
+ let(:state) { Smartdown::Engine::State.new(current_node: "n") }
230
+
231
+ it "raises an UndefinedValue error" do
232
+ predicates.values.each do |predicate|
233
+ expect { evaluator.evaluate(predicate, state) }.to raise_error(Smartdown::Engine::UndefinedValue)
234
+ end
235
+ end
236
+ end
237
+
238
+ context "state has lower value" do
239
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-1-30") }
240
+ let(:results) { {
241
+ :greater => false,
242
+ :greater_or_equal => false,
243
+ :less => true,
244
+ :less_or_equal => true
245
+ } }
246
+ it "evaluates correctly" do
247
+ predicates.each do |predicate_key, predicate|
248
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
249
+ end
250
+ end
251
+ end
252
+
253
+ context "state has identical value" do
254
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-1-31") }
255
+ let(:results) { {
256
+ :greater => false,
257
+ :greater_or_equal => true,
258
+ :less => false,
259
+ :less_or_equal => true
260
+ } }
261
+ it "evaluates correctly" do
262
+ predicates.each do |predicate_key, predicate|
263
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
264
+ end
265
+ end
266
+ end
267
+
268
+ context "state has higher value" do
269
+ let(:state) { Smartdown::Engine::State.new(current_node: "n", my_var: "2014-2-1") }
270
+ let(:results) { {
271
+ :greater => true,
272
+ :greater_or_equal => true,
273
+ :less => false,
274
+ :less_or_equal => false
275
+ } }
276
+ it "evaluates correctly" do
277
+ predicates.each do |predicate_key, predicate|
278
+ expect(evaluator.evaluate(predicate, state)).to eq(results[predicate_key])
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
98
284
  end
@@ -0,0 +1,28 @@
1
+ require 'smartdown/parser/node_parser'
2
+ require 'smartdown/parser/node_interpreter'
3
+ require 'smartdown/parser/element/date_question'
4
+
5
+ describe Smartdown::Parser::Element::DateQuestion do
6
+ subject(:parser) { described_class.new }
7
+
8
+ context "with question tag" do
9
+ let(:source) { "[date: date_of_birth]" }
10
+
11
+ it "parses" do
12
+ should parse(source).as(
13
+ date: {
14
+ identifier: "date_of_birth",
15
+ }
16
+ )
17
+ end
18
+
19
+ describe "transformed" do
20
+ let(:node_name) { "my_node" }
21
+ subject(:transformed) {
22
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
23
+ }
24
+
25
+ it { should eq(Smartdown::Model::Element::Question::Date.new("date_of_birth")) }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'smartdown/parser/node_parser'
2
+ require 'smartdown/parser/node_interpreter'
3
+ require 'smartdown/parser/element/salary_question'
4
+
5
+ describe Smartdown::Parser::Element::SalaryQuestion do
6
+ subject(:parser) { described_class.new }
7
+
8
+ context "with question tag" do
9
+ let(:source) { "[salary: mother_salary]" }
10
+
11
+ it "parses" do
12
+ should parse(source).as(
13
+ salary: {
14
+ identifier: "mother_salary",
15
+ }
16
+ )
17
+ end
18
+
19
+ describe "transformed" do
20
+ let(:node_name) { "my_node" }
21
+ subject(:transformed) {
22
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
23
+ }
24
+
25
+ it { should eq(Smartdown::Model::Element::Question::Salary.new("mother_salary")) }
26
+ end
27
+ end
28
+ end
@@ -61,5 +61,84 @@ describe Smartdown::Parser::Predicates do
61
61
  it { should eq(Smartdown::Model::Predicate::Named.new("my_pred")) }
62
62
  end
63
63
  end
64
+
65
+ describe "predicate AND predicate" do
66
+ subject(:parser) { described_class.new }
67
+
68
+ it { should parse("my_pred AND my_other_pred").as(
69
+ { combined_predicate: {
70
+ first_predicate: { named_predicate: "my_pred" },
71
+ and_predicates:
72
+ [
73
+ {named_predicate: "my_other_pred"},
74
+ ]
75
+ } }
76
+ ) }
77
+ it { should parse("my_pred AND my_other_pred AND varname in {a b c}").as(
78
+ { combined_predicate: {
79
+ first_predicate: { named_predicate: "my_pred" },
80
+ and_predicates:
81
+ [
82
+ {named_predicate: "my_other_pred"},
83
+ {set_membership_predicate:
84
+ {varname: "varname", values: [{set_value: "a"}, {set_value: "b"}, {set_value: "c"}]}
85
+ }
86
+ ]
87
+ } }
88
+ ) }
89
+ it { should_not parse("my_pred AND ") }
90
+
91
+ describe "transformed" do
92
+ let(:node_name) { "my_node" }
93
+ let(:source) { "my_pred AND my_other_pred" }
94
+ subject(:transformed) {
95
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
96
+ }
97
+
98
+ it { should eq(Smartdown::Model::Predicate::Combined.new(
99
+ [
100
+ Smartdown::Model::Predicate::Named.new("my_pred"),
101
+ Smartdown::Model::Predicate::Named.new("my_other_pred")
102
+ ]
103
+ )) }
104
+ end
105
+ end
106
+
107
+ describe "comparison predicate" do
108
+ subject(:parser) { described_class.new }
109
+ let(:greater_equal_source) { "varname >= 'value'" }
110
+ let(:greater_source) { "varname > 'value'" }
111
+ let(:less_equal_source) { "varname <= 'value'" }
112
+ let(:less_source) { "varname < 'value'" }
113
+
114
+ it { should parse(greater_equal_source).as(comparison_predicate: {varname: "varname", value: "value", operator: ">="}) }
115
+ it { should_not parse("v >= value") }
116
+ it { should_not parse("v >= 'a thing's thing'") }
117
+ it { should_not parse("v >= 'a thing\\'s thing'") }
118
+ it { should_not parse(%q{v >= "a thing"}) }
119
+
120
+ describe "transformed" do
121
+ let(:node_name) { "my_node" }
122
+ subject(:transformed) {
123
+ Smartdown::Parser::NodeInterpreter.new(node_name, source, parser: parser).interpret
124
+ }
125
+ context "greater" do
126
+ let(:source) { greater_equal_source }
127
+ it { should eq(Smartdown::Model::Predicate::Comparison::GreaterOrEqual.new("varname", "value")) }
128
+ end
129
+ context "stricly greater" do
130
+ let(:source) { greater_source }
131
+ it { should eq(Smartdown::Model::Predicate::Comparison::Greater.new("varname", "value")) }
132
+ end
133
+ context "lower" do
134
+ let(:source) { less_equal_source }
135
+ it { should eq(Smartdown::Model::Predicate::Comparison::LessOrEqual.new("varname", "value")) }
136
+ end
137
+ context "strictly lower" do
138
+ let(:source) { less_source }
139
+ it { should eq(Smartdown::Model::Predicate::Comparison::Less.new("varname", "value")) }
140
+ end
141
+ end
142
+ end
64
143
  end
65
144
 
@@ -4,12 +4,19 @@ require 'smartdown/model/element/markdown_heading'
4
4
  require 'smartdown/model/element/markdown_paragraph'
5
5
  require 'smartdown/model/element/start_button'
6
6
  require 'smartdown/model/element/question/multiple_choice'
7
+ require 'smartdown/model/element/question/date'
8
+ require 'smartdown/model/element/question/salary'
7
9
  require 'smartdown/model/element/conditional'
8
10
  require 'smartdown/model/next_node_rules'
9
11
  require 'smartdown/model/rule'
10
12
  require 'smartdown/model/predicate/named'
11
13
  require 'smartdown/model/predicate/equality'
12
14
  require 'smartdown/model/predicate/set_membership'
15
+ require 'smartdown/model/predicate/combined'
16
+ require 'smartdown/model/predicate/comparison/greater_or_equal'
17
+ require 'smartdown/model/predicate/comparison/less_or_equal'
18
+ require 'smartdown/model/predicate/comparison/greater'
19
+ require 'smartdown/model/predicate/comparison/less'
13
20
 
14
21
  class ModelBuilder
15
22
  def flow(name, &block)
@@ -52,6 +59,12 @@ class ModelBuilder
52
59
  @elements.last
53
60
  end
54
61
 
62
+ def date(name)
63
+ @elements ||= []
64
+ @elements << Smartdown::Model::Element::Question::Date.new(name)
65
+ @elements.last
66
+ end
67
+
55
68
  def next_steps(urls)
56
69
  @elements ||= []
57
70
  urls_with_string_keys = ::Hash[urls.map {|k,v| [k.to_s, v]}]
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.2.0
4
+ version: 0.2.1
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: &18315460 !ruby/object:Gem::Requirement
16
+ requirement: &13419100 !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: *18315460
24
+ version_requirements: *13419100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &18314660 !ruby/object:Gem::Requirement
27
+ requirement: &13417020 !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: *18314660
35
+ version_requirements: *13417020
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &18314100 !ruby/object:Gem::Requirement
38
+ requirement: &13819280 !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: *18314100
46
+ version_requirements: *13819280
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: gem_publisher
49
- requirement: &18313460 !ruby/object:Gem::Requirement
49
+ requirement: &13818580 !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: *18313460
57
+ version_requirements: *13818580
58
58
  description:
59
59
  email: david.heath@digital.cabinet-office.gov.uk
60
60
  executables:
@@ -71,6 +71,8 @@ files:
71
71
  - lib/smartdown/parser/directory_input.rb
72
72
  - lib/smartdown/parser/rules.rb
73
73
  - lib/smartdown/parser/node_parser.rb
74
+ - lib/smartdown/parser/element/date_question.rb
75
+ - lib/smartdown/parser/element/salary_question.rb
74
76
  - lib/smartdown/parser/element/conditional.rb
75
77
  - lib/smartdown/parser/element/markdown_paragraph.rb
76
78
  - lib/smartdown/parser/element/start_button.rb
@@ -79,8 +81,10 @@ files:
79
81
  - lib/smartdown/parser/element/next_steps.rb
80
82
  - lib/smartdown/parser/element/markdown_heading.rb
81
83
  - lib/smartdown/parser/flow_interpreter.rb
84
+ - lib/smartdown/api/date_question.rb
82
85
  - lib/smartdown/api/coversheet.rb
83
86
  - lib/smartdown/api/question.rb
87
+ - lib/smartdown/api/salary_question.rb
84
88
  - lib/smartdown/api/flow.rb
85
89
  - lib/smartdown/api/question_page.rb
86
90
  - lib/smartdown/api/state.rb
@@ -102,6 +106,12 @@ files:
102
106
  - lib/smartdown/model/predicate/named.rb
103
107
  - lib/smartdown/model/predicate/set_membership.rb
104
108
  - lib/smartdown/model/predicate/equality.rb
109
+ - lib/smartdown/model/predicate/comparison/less_or_equal.rb
110
+ - lib/smartdown/model/predicate/comparison/greater_or_equal.rb
111
+ - lib/smartdown/model/predicate/comparison/base.rb
112
+ - lib/smartdown/model/predicate/comparison/greater.rb
113
+ - lib/smartdown/model/predicate/comparison/less.rb
114
+ - lib/smartdown/model/predicate/combined.rb
105
115
  - lib/smartdown/model/node.rb
106
116
  - lib/smartdown/model/front_matter.rb
107
117
  - lib/smartdown/model/element/conditional.rb
@@ -109,6 +119,8 @@ files:
109
119
  - lib/smartdown/model/element/start_button.rb
110
120
  - lib/smartdown/model/element/next_steps.rb
111
121
  - lib/smartdown/model/element/markdown_heading.rb
122
+ - lib/smartdown/model/element/question/date.rb
123
+ - lib/smartdown/model/element/question/salary.rb
112
124
  - lib/smartdown/model/element/question/multiple_choice.rb
113
125
  - lib/smartdown/model/next_node_rules.rb
114
126
  - lib/smartdown/model/nested_rule.rb
@@ -127,9 +139,11 @@ files:
127
139
  - spec/parser/element/multiple_choice_question_spec.rb
128
140
  - spec/parser/element/conditional_spec.rb
129
141
  - spec/parser/element/start_button_parser_spec.rb
142
+ - spec/parser/element/date_question_spec.rb
130
143
  - spec/parser/element/markdown_paragraph_spec.rb
131
144
  - spec/parser/element/markdown_heading_spec.rb
132
145
  - spec/parser/element/front_matter_spec.rb
146
+ - spec/parser/element/salary_question_spec.rb
133
147
  - spec/parser/node_parser_spec.rb
134
148
  - spec/support/model_builder.rb
135
149
  - spec/spec_helper.rb
@@ -168,7 +182,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
168
182
  version: '0'
169
183
  segments:
170
184
  - 0
171
- hash: -3191894024105276380
185
+ hash: -352555312520548432
172
186
  required_rubygems_version: !ruby/object:Gem::Requirement
173
187
  none: false
174
188
  requirements:
@@ -177,7 +191,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
191
  version: '0'
178
192
  segments:
179
193
  - 0
180
- hash: -3191894024105276380
194
+ hash: -352555312520548432
181
195
  requirements: []
182
196
  rubyforge_project:
183
197
  rubygems_version: 1.8.11
@@ -196,9 +210,11 @@ test_files:
196
210
  - spec/parser/element/multiple_choice_question_spec.rb
197
211
  - spec/parser/element/conditional_spec.rb
198
212
  - spec/parser/element/start_button_parser_spec.rb
213
+ - spec/parser/element/date_question_spec.rb
199
214
  - spec/parser/element/markdown_paragraph_spec.rb
200
215
  - spec/parser/element/markdown_heading_spec.rb
201
216
  - spec/parser/element/front_matter_spec.rb
217
+ - spec/parser/element/salary_question_spec.rb
202
218
  - spec/parser/node_parser_spec.rb
203
219
  - spec/support/model_builder.rb
204
220
  - spec/spec_helper.rb