smartdown 0.3.0 → 0.4.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.
Files changed (42) hide show
  1. data/lib/smartdown/engine/conditional_resolver.rb +1 -9
  2. data/lib/smartdown/engine/interpolator.rb +12 -1
  3. data/lib/smartdown/engine/state.rb +1 -10
  4. data/lib/smartdown/engine/transition.rb +2 -6
  5. data/lib/smartdown/engine.rb +2 -3
  6. data/lib/smartdown/model/predicate/combined.rb +9 -1
  7. data/lib/smartdown/model/predicate/comparison/base.rb +4 -1
  8. data/lib/smartdown/model/predicate/comparison/greater.rb +6 -1
  9. data/lib/smartdown/model/predicate/comparison/greater_or_equal.rb +6 -1
  10. data/lib/smartdown/model/predicate/comparison/less.rb +6 -1
  11. data/lib/smartdown/model/predicate/comparison/less_or_equal.rb +6 -1
  12. data/lib/smartdown/model/predicate/equality.rb +9 -1
  13. data/lib/smartdown/model/predicate/function.rb +28 -0
  14. data/lib/smartdown/model/predicate/named.rb +9 -1
  15. data/lib/smartdown/model/predicate/otherwise.rb +15 -0
  16. data/lib/smartdown/model/predicate/set_membership.rb +9 -1
  17. data/lib/smartdown/parser/node_transform.rb +14 -0
  18. data/lib/smartdown/parser/predicates.rb +18 -1
  19. data/lib/smartdown/version.rb +1 -1
  20. data/spec/acceptance/parsing_spec.rb +8 -0
  21. data/spec/engine/interpolator_spec.rb +14 -0
  22. data/spec/engine/state_spec.rb +0 -37
  23. data/spec/engine/transition_spec.rb +23 -19
  24. data/spec/engine_spec.rb +8 -11
  25. data/spec/fixtures/acceptance/animal-example-simple/animal-example-simple.txt +25 -0
  26. data/spec/fixtures/acceptance/animal-example-simple/outcomes/outcome_safe_pet.txt +69 -0
  27. data/spec/fixtures/acceptance/animal-example-simple/outcomes/outcome_tigers_are_fine.txt +9 -0
  28. data/spec/fixtures/acceptance/animal-example-simple/outcomes/outcome_trained_with_lions.txt +3 -0
  29. data/spec/fixtures/acceptance/animal-example-simple/outcomes/outcome_untrained_with_lions.txt +3 -0
  30. data/spec/fixtures/acceptance/animal-example-simple/questions/question_1.txt +13 -0
  31. data/spec/fixtures/acceptance/animal-example-simple/questions/question_2.txt +10 -0
  32. data/spec/model/predicates/combined_spec.rb +47 -0
  33. data/spec/model/predicates/comparison_spec.rb +162 -0
  34. data/spec/model/predicates/equality_spec.rb +36 -0
  35. data/spec/model/predicates/function_spec.rb +85 -0
  36. data/spec/model/predicates/named_spec.rb +41 -0
  37. data/spec/model/predicates/set_membership_spec.rb +39 -0
  38. data/spec/parser/predicates_spec.rb +98 -16
  39. data/spec/support/model_builder.rb +4 -0
  40. metadata +39 -14
  41. data/lib/smartdown/engine/predicate_evaluator.rb +0 -27
  42. data/spec/engine/predicate_evaluator_spec.rb +0 -284
@@ -1,12 +1,6 @@
1
- require 'smartdown/engine/predicate_evaluator'
2
-
3
1
  module Smartdown
4
2
  class Engine
5
3
  class ConditionalResolver
6
- def initialize(predicate_evaluator = nil)
7
- @predicate_evaluator = predicate_evaluator || PredicateEvaluator.new
8
- end
9
-
10
4
  def call(node, state)
11
5
  node.dup.tap do |new_node|
12
6
  new_node.elements = resolve_conditionals(node.elements, state)
@@ -14,10 +8,8 @@ module Smartdown
14
8
  end
15
9
 
16
10
  private
17
- attr_accessor :predicate_evaluator
18
-
19
11
  def evaluate(conditional, state)
20
- if predicate_evaluator.evaluate(conditional.predicate, state)
12
+ if conditional.predicate.evaluate(state)
21
13
  conditional.true_case
22
14
  else
23
15
  conditional.false_case
@@ -39,7 +39,18 @@ module Smartdown
39
39
 
40
40
  private
41
41
  def interpolate(text, state)
42
- text.to_s.gsub(/%{([^}]+)}/) { |_| state.get($1) }
42
+ text.to_s.gsub(/%{([^}]+)}/) do |_|
43
+ resolve_term($1, state)
44
+ end
45
+ end
46
+
47
+ def resolve_term(interpolation, state)
48
+ begin
49
+ parsed = Smartdown::Parser::Predicates.new.parse(interpolation)
50
+ Smartdown::Parser::NodeTransform.new.apply(parsed, {}).evaluate(state)
51
+ rescue Parslet::ParseFailed
52
+ state.get(interpolation)
53
+ end
43
54
  end
44
55
  end
45
56
 
@@ -22,12 +22,7 @@ module Smartdown
22
22
  end
23
23
 
24
24
  def get(key)
25
- value = fetch(key)
26
- if value.respond_to?(:call)
27
- evaluate(value)
28
- else
29
- value
30
- end
25
+ fetch(key)
31
26
  end
32
27
 
33
28
  def put(name, value)
@@ -54,10 +49,6 @@ module Smartdown
54
49
  raise UndefinedValue, "variable '#{key}' not defined", caller
55
50
  end
56
51
  end
57
-
58
- def evaluate(callable)
59
- @cached[callable] ||= callable.call(self)
60
- end
61
52
  end
62
53
  end
63
54
  end
@@ -1,4 +1,3 @@
1
- require 'smartdown/engine/predicate_evaluator'
2
1
  require 'smartdown/engine/errors'
3
2
 
4
3
  module Smartdown
@@ -10,7 +9,6 @@ module Smartdown
10
9
  @state = state
11
10
  @node = node
12
11
  @inputs = input_array
13
- @predicate_evaluator = options[:predicate_evaluator] || PredicateEvaluator.new
14
12
  end
15
13
 
16
14
  def next_node
@@ -27,8 +25,6 @@ module Smartdown
27
25
  end
28
26
 
29
27
  private
30
- attr_reader :predicate_evaluator
31
-
32
28
  def next_node_from_next_node_rules
33
29
  next_node_rules && first_matching_rule(next_node_rules.rules).outcome
34
30
  end
@@ -60,11 +56,11 @@ module Smartdown
60
56
  rules.each do |rule|
61
57
  case rule
62
58
  when Smartdown::Model::Rule
63
- if predicate_evaluator.evaluate(rule.predicate, state_with_inputs)
59
+ if rule.predicate.evaluate(state_with_inputs)
64
60
  throw(:match, rule)
65
61
  end
66
62
  when Smartdown::Model::NestedRule
67
- if predicate_evaluator.evaluate(rule.predicate, state_with_inputs)
63
+ if rule.predicate.evaluate(state_with_inputs)
68
64
  throw_first_matching_rule_in(rule.children)
69
65
  end
70
66
  else
@@ -1,6 +1,7 @@
1
1
  require 'smartdown/engine/transition'
2
2
  require 'smartdown/engine/state'
3
3
  require 'smartdown/engine/node_presenter'
4
+ require 'smartdown/model/predicate/otherwise'
4
5
 
5
6
  module Smartdown
6
7
  class Engine
@@ -20,9 +21,7 @@ module Smartdown
20
21
  end
21
22
 
22
23
  def default_predicates
23
- {
24
- otherwise: ->(_) { true }
25
- }.merge(@initial_state)
24
+ {}.merge(@initial_state)
26
25
  end
27
26
 
28
27
  def process(responses, test_start_state = nil)
@@ -1,7 +1,15 @@
1
1
  module Smartdown
2
2
  module Model
3
3
  module Predicate
4
- Combined = Struct.new(:predicates)
4
+ Combined = Struct.new(:predicates) do
5
+ def evaluate(state)
6
+ predicates.map { |predicate| predicate.evaluate(state) }.all?
7
+ end
8
+
9
+ def humanize
10
+ "(#{predicates.map { |p| p.humanize }.join(' AND ')})"
11
+ end
12
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -2,7 +2,10 @@ module Smartdown
2
2
  module Model
3
3
  module Predicate
4
4
  module Comparison
5
- Base = Struct.new(:varname, :value)
5
+ Base = Struct.new(:varname, :value) do
6
+ def evaluate(state)
7
+ end
8
+ end
6
9
  end
7
10
  end
8
11
  end
@@ -6,13 +6,18 @@ module Smartdown
6
6
  module Predicate
7
7
  module Comparison
8
8
  class Greater < Base
9
- def evaluate(variable)
9
+ def evaluate(state)
10
+ variable = state.get(varname)
10
11
  if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
12
  Date.parse(variable) > Date.parse(value)
12
13
  else
13
14
  variable > value
14
15
  end
15
16
  end
17
+
18
+ def humanize
19
+ "#{varname} > #{value}"
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -6,13 +6,18 @@ module Smartdown
6
6
  module Predicate
7
7
  module Comparison
8
8
  class GreaterOrEqual < Base
9
- def evaluate(variable)
9
+ def evaluate(state)
10
+ variable = state.get(varname)
10
11
  if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
12
  Date.parse(variable) >= Date.parse(value)
12
13
  else
13
14
  variable >= value
14
15
  end
15
16
  end
17
+
18
+ def humanize
19
+ "#{varname} >= #{value}"
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -6,13 +6,18 @@ module Smartdown
6
6
  module Predicate
7
7
  module Comparison
8
8
  class Less < Base
9
- def evaluate(variable)
9
+ def evaluate(state)
10
+ variable = state.get(varname)
10
11
  if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
12
  Date.parse(variable) < Date.parse(value)
12
13
  else
13
14
  variable < value
14
15
  end
15
16
  end
17
+
18
+ def humanize
19
+ "#{varname} < #{value}"
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -6,13 +6,18 @@ module Smartdown
6
6
  module Predicate
7
7
  module Comparison
8
8
  class LessOrEqual < Base
9
- def evaluate(variable)
9
+ def evaluate(state)
10
+ variable = state.get(varname)
10
11
  if /(\d{4})-(\d{1,2})-(\d{1,2})/.match(value)
11
12
  Date.parse(variable) <= Date.parse(value)
12
13
  else
13
14
  variable <= value
14
15
  end
15
16
  end
17
+
18
+ def humanize
19
+ "#{varname} <= #{value}"
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -1,7 +1,15 @@
1
1
  module Smartdown
2
2
  module Model
3
3
  module Predicate
4
- Equality = Struct.new(:varname, :expected_value)
4
+ Equality = Struct.new(:varname, :expected_value) do
5
+ def evaluate(state)
6
+ state.get(varname) == expected_value
7
+ end
8
+
9
+ def humanize
10
+ "#{varname} == '#{expected_value}'"
11
+ end
12
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -0,0 +1,28 @@
1
+ module Smartdown
2
+ module Model
3
+ module Predicate
4
+ Function = Struct.new(:name, :arguments) do
5
+ def evaluate(state)
6
+ state.get(name).call(*evaluate_arguments(state))
7
+ end
8
+
9
+ def humanize
10
+ "#{name}(#{arguments.join(' ')})"
11
+ end
12
+
13
+ private
14
+
15
+ def evaluate_arguments(state)
16
+ # Should have an evaluatable "variable" object that matches identifier
17
+ arguments.map do |argument|
18
+ if argument.respond_to?(:evaluate)
19
+ argument.evaluate(state)
20
+ else
21
+ state.get(argument)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -1,7 +1,15 @@
1
1
  module Smartdown
2
2
  module Model
3
3
  module Predicate
4
- Named = Struct.new(:name)
4
+ Named = Struct.new(:name) do
5
+ def evaluate(state)
6
+ !!state.get(name)
7
+ end
8
+
9
+ def humanize
10
+ "#{name.to_s.chomp('?')}?"
11
+ end
12
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -0,0 +1,15 @@
1
+ module Smartdown
2
+ module Model
3
+ module Predicate
4
+ class Otherwise
5
+ def evaluate(state)
6
+ true
7
+ end
8
+
9
+ def ==(o)
10
+ o.class == self.class
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,15 @@
1
1
  module Smartdown
2
2
  module Model
3
3
  module Predicate
4
- SetMembership = Struct.new(:varname, :values)
4
+ SetMembership = Struct.new(:varname, :values) do
5
+ def evaluate(state)
6
+ values.include?(state.get(varname))
7
+ end
8
+
9
+ def humanize
10
+ "#{varname} in [#{values.join(", ")}]"
11
+ end
12
+ end
5
13
  end
6
14
  end
7
15
  end
@@ -16,6 +16,7 @@ require 'smartdown/model/predicate/equality'
16
16
  require 'smartdown/model/predicate/set_membership'
17
17
  require 'smartdown/model/predicate/named'
18
18
  require 'smartdown/model/predicate/combined'
19
+ require 'smartdown/model/predicate/function'
19
20
  require 'smartdown/model/predicate/comparison/greater_or_equal'
20
21
  require 'smartdown/model/predicate/comparison/greater'
21
22
  require 'smartdown/model/predicate/comparison/less_or_equal'
@@ -120,11 +121,24 @@ module Smartdown
120
121
  Smartdown::Model::Predicate::Named.new(name)
121
122
  }
122
123
 
124
+ rule(:otherwise_predicate => simple(:name) ) {
125
+ Smartdown::Model::Predicate::Otherwise.new
126
+ }
123
127
 
124
128
  rule(:combined_predicate => {first_predicate: subtree(:first_predicate), and_predicates: subtree(:and_predicates) }) {
125
129
  Smartdown::Model::Predicate::Combined.new([first_predicate]+and_predicates)
126
130
  }
127
131
 
132
+ rule(:function_argument => simple(:argument)) { argument }
133
+
134
+ rule(:function_predicate => { name: simple(:name), arguments: subtree(:arguments) }) {
135
+ Smartdown::Model::Predicate::Function.new(name, Array(arguments))
136
+ }
137
+
138
+ rule(:function_predicate => { name: simple(:name) }) {
139
+ Smartdown::Model::Predicate::Function.new(name, [])
140
+ }
141
+
128
142
  rule(:comparison_predicate => { varname: simple(:varname),
129
143
  value: simple(:value),
130
144
  operator: simple(:operator)
@@ -34,13 +34,18 @@ module Smartdown
34
34
  }
35
35
 
36
36
  rule(:named_predicate) {
37
- question_identifier.as(:named_predicate)
37
+ (identifier >> str('?')).as(:named_predicate)
38
+ }
39
+
40
+ rule(:otherwise_predicate) {
41
+ str('otherwise').as(:otherwise_predicate)
38
42
  }
39
43
 
40
44
  rule(:predicate) {
41
45
  equality_predicate.as(:equality_predicate) |
42
46
  set_membership_predicate.as(:set_membership_predicate) |
43
47
  comparison_predicate.as(:comparison_predicate) |
48
+ otherwise_predicate |
44
49
  named_predicate
45
50
  }
46
51
 
@@ -50,8 +55,20 @@ module Smartdown
50
55
  predicate).repeat(1).as(:and_predicates)
51
56
  }
52
57
 
58
+ rule(:function_arguments) {
59
+ identifier.as(:function_argument) >> (some_space >> identifier.as(:function_argument)).repeat
60
+ }
61
+
62
+ rule (:function_predicate) {
63
+ identifier.as(:name) >>
64
+ str('(') >>
65
+ function_arguments.as(:arguments).maybe >>
66
+ str(')')
67
+ }
68
+
53
69
  rule (:predicates) {
54
70
  combined_predicate.as(:combined_predicate) |
71
+ function_predicate.as(:function_predicate) |
55
72
  predicate
56
73
  }
57
74
 
@@ -1,3 +1,3 @@
1
1
  module Smartdown
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -104,6 +104,14 @@ EXPECTED
104
104
  expect(flow.nodes.map(&:name)).to eq(["question-and-outcome", "q1", "o1"])
105
105
  end
106
106
  end
107
+
108
+ context "full flow example" do
109
+ subject(:flow) { Smartdown.parse(fixture("animal-example-simple")) }
110
+
111
+ it "parses" do
112
+ expect(flow.instance_of? Smartdown::Model::Flow).to eq(true)
113
+ end
114
+ end
107
115
  end
108
116
 
109
117
 
@@ -78,4 +78,18 @@ describe Smartdown::Engine::Interpolator do
78
78
  expect(interpolated_node.elements.first.content).to eq("Hello #{example_name}")
79
79
  end
80
80
  end
81
+
82
+ context "a paragraph containing function call" do
83
+ let(:elements) { [Smartdown::Model::Element::MarkdownParagraph.new('%{double(number)}')] }
84
+ let(:state) {
85
+ Smartdown::Engine::State.new(
86
+ current_node: node.name,
87
+ number: 10,
88
+ double: ->(number) { number * 2 }
89
+ )
90
+ }
91
+ it "interpolates the result of the function call" do
92
+ expect(interpolated_node.elements.first.content).to eq("20")
93
+ end
94
+ end
81
95
  end
@@ -48,43 +48,6 @@ describe Smartdown::Engine::State do
48
48
  end
49
49
  end
50
50
 
51
- context "lambda values" do
52
- let(:predicate) { double("predicate", call: true) }
53
- subject(:state) {
54
- described_class.new(current_node: :start_state, predicate?: predicate)
55
- }
56
-
57
- describe "#get" do
58
- it "evaluates lambda with state" do
59
- expect(predicate).to receive(:call).with(state).and_return(true)
60
- expect(state.get(:predicate?)).to eq(true)
61
- end
62
-
63
- it "caches the result of evaluating the lambda" do
64
- expect(predicate).to receive(:call).once
65
- state.get(:predicate?)
66
- state.get(:predicate?)
67
- end
68
- end
69
-
70
- describe "#==" do
71
- let(:l1) { ->(state) { true } }
72
- let(:l2) { ->(state) { true } }
73
-
74
- let(:state_with_l1a) { described_class.new(current_node: "red", pred: l1)}
75
- let(:state_with_l1b) { described_class.new(current_node: "red", pred: l1)}
76
- let(:state_with_l2) { described_class.new(current_node: "red", pred: l2)}
77
-
78
- it "is equal if identical lambdas" do
79
- expect(state_with_l1a).to eq(state_with_l1b)
80
- end
81
-
82
- it "is not equal if different lambdas" do
83
- expect(state_with_l1a).not_to eq(state_with_l2)
84
- end
85
- end
86
- end
87
-
88
51
  describe "#==" do
89
52
  let(:s1) { described_class.new(current_node: "red") }
90
53
  let(:s2) { described_class.new(current_node: "red") }
@@ -8,8 +8,7 @@ describe Smartdown::Engine::Transition do
8
8
  let(:start_state) { Smartdown::Engine::State.new(current_node: current_node_name) }
9
9
  let(:input) { "yes" }
10
10
  let(:input_array) { [input] }
11
- subject(:transition) { described_class.new(start_state, current_node, input_array, predicate_evaluator: predicate_evaluator) }
12
- let(:predicate_evaluator) { instance_double("Smartdown::Engine::PredicateEvaluator", evaluate: true) }
11
+ subject(:transition) { described_class.new(start_state, current_node, input_array) }
13
12
  let(:state_including_input) {
14
13
  start_state.put(current_node.name, input_array)
15
14
  }
@@ -65,22 +64,22 @@ describe Smartdown::Engine::Transition do
65
64
  }
66
65
 
67
66
  describe "#next_node" do
68
- it "invokes the predicate evaluator with the predicate and state including the input value" do
69
- expect(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
67
+ it "evaluates the predicates with state including the input value" do
68
+ expect(predicate1).to receive(:evaluate).with(state_including_input).and_return(true)
70
69
 
71
70
  transition.next_node
72
71
  end
73
72
 
74
- it "invokes the predicate evaluator for each rule in turn" do
75
- allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
76
- expect(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
73
+ it "evaluates the predicates for each rule in turn" do
74
+ allow(predicate1).to receive(:evaluate).with(state_including_input).and_return(false)
75
+ expect(predicate2).to receive(:evaluate).with(state_including_input).and_return(true)
77
76
 
78
77
  transition.next_node
79
78
  end
80
79
 
81
80
  it "returns the name of the next node" do
82
- allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
83
- allow(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
81
+ allow(predicate1).to receive(:evaluate).with(state_including_input).and_return(false)
82
+ allow(predicate2).to receive(:evaluate).with(state_including_input).and_return(true)
84
83
 
85
84
  expect(transition.next_node).to eq(outcome_name2)
86
85
  end
@@ -94,6 +93,8 @@ describe Smartdown::Engine::Transition do
94
93
  .put(:current_node, outcome_name1)
95
94
  .put(current_node.name, input_array)
96
95
 
96
+ allow(predicate1).to receive(:evaluate).with(state_including_input).and_return(true)
97
+
97
98
  expect(transition.next_state).to eq(expected_state)
98
99
  end
99
100
  end
@@ -130,27 +131,27 @@ describe Smartdown::Engine::Transition do
130
131
 
131
132
  describe "#next_node" do
132
133
  context "p1 false" do
133
- it "invokes the predicate evaluator with each predicate in turn" do
134
- expect(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(false)
134
+ it "evaluates each predicate in turn" do
135
+ expect(predicate1).to receive(:evaluate).with(state_including_input).and_return(false)
135
136
 
136
137
  expect { transition.next_node }.to raise_error(Smartdown::Engine::IndeterminateNextNode)
137
138
  end
138
139
  end
139
140
 
140
141
  context "p1 and p2 true" do
141
- it "invokes the predicate evaluator with each predicate in turn" do
142
- allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
143
- expect(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(true)
142
+ it "evaluates each predicate in turn" do
143
+ allow(predicate1).to receive(:evaluate).with(state_including_input).and_return(true)
144
+ expect(predicate2).to receive(:evaluate).with(state_including_input).and_return(true)
144
145
 
145
146
  expect(transition.next_node).to eq(outcome_name1)
146
147
  end
147
148
  end
148
149
 
149
150
  context "p1 true, p2 false, p3 true" do
150
- it "invokes the predicate evaluator with each predicate in turn" do
151
- allow(predicate_evaluator).to receive(:evaluate).with(predicate1, state_including_input).and_return(true)
152
- allow(predicate_evaluator).to receive(:evaluate).with(predicate2, state_including_input).and_return(false)
153
- expect(predicate_evaluator).to receive(:evaluate).with(predicate3, state_including_input).and_return(true)
151
+ it "evaluates each predicate in turn" do
152
+ allow(predicate1).to receive(:evaluate).with(state_including_input).and_return(true)
153
+ allow(predicate2).to receive(:evaluate).with(state_including_input).and_return(false)
154
+ expect(predicate3).to receive(:evaluate).with(state_including_input).and_return(true)
154
155
 
155
156
  expect(transition.next_node).to eq(outcome_name2)
156
157
  end
@@ -160,6 +161,7 @@ describe Smartdown::Engine::Transition do
160
161
 
161
162
  context "next node rules and a named question" do
162
163
  let(:question_name) { "my_question" }
164
+ let(:predicate1) { double("predicate1") }
163
165
 
164
166
  let(:current_node) {
165
167
  Smartdown::Model::Node.new(
@@ -167,7 +169,7 @@ describe Smartdown::Engine::Transition do
167
169
  [
168
170
  Smartdown::Model::Element::Question::MultipleChoice.new(question_name, {"a" => "Apple"}),
169
171
  Smartdown::Model::NextNodeRules.new(
170
- [Smartdown::Model::Rule.new(double("predicate1"), "o1")]
172
+ [Smartdown::Model::Rule.new(predicate1, "o1")]
171
173
  )
172
174
  ]
173
175
  )
@@ -175,6 +177,8 @@ describe Smartdown::Engine::Transition do
175
177
 
176
178
  describe "#next_state" do
177
179
  it "assigns the input value to a variable matching the question name" do
180
+ allow(predicate1).to receive(:evaluate).and_return(true)
181
+
178
182
  expect(transition.next_state.get(question_name)).to eq(input)
179
183
  end
180
184
  end
data/spec/engine_spec.rb CHANGED
@@ -5,12 +5,6 @@ describe Smartdown::Engine do
5
5
  subject(:engine) { Smartdown::Engine.new(flow) }
6
6
  let(:start_state) {
7
7
  engine.build_start_state
8
- .put(:eea_passport?, ->(state) {
9
- %w{greek british}.include?(state.get(:what_passport_do_you_have?))
10
- })
11
- .put(:imaginary?, ->(state) {
12
- %w{narnia}.include?(state.get(:what_country_are_you_going_to?))
13
- })
14
8
  .put(:otherwise, true)
15
9
  }
16
10
 
@@ -34,7 +28,7 @@ describe Smartdown::Engine do
34
28
  )
35
29
  next_node_rules do
36
30
  rule do
37
- named_predicate("eea_passport?")
31
+ set_membership_predicate("what_passport_do_you_have?", ["greek", "british"])
38
32
  outcome("outcome_no_visa_needed")
39
33
  end
40
34
  end
@@ -85,11 +79,11 @@ describe Smartdown::Engine do
85
79
  )
86
80
  next_node_rules do
87
81
  rule do
88
- named_predicate("imaginary?")
82
+ set_membership_predicate("what_country_are_you_going_to?", ["narnia"])
89
83
  outcome("outcome_imaginary_country")
90
84
  end
91
85
  rule do
92
- named_predicate("eea_passport?")
86
+ set_membership_predicate("what_passport_do_you_have?", ["greek", "british"])
93
87
  outcome("outcome_no_visa_needed")
94
88
  end
95
89
  end
@@ -118,16 +112,19 @@ describe Smartdown::Engine do
118
112
  }
119
113
 
120
114
  describe "initial_state" do
115
+ let(:lambda) {
116
+ ->(state) { 'a_dynamic_state_object' }
117
+ }
121
118
  let(:initial_state) { {
122
119
  key_1: 'a_state_object',
123
- key_2: ->(state) { 'a_dynamic_state_object' }
120
+ key_2: :lambda
124
121
  } }
125
122
  let(:engine) { Smartdown::Engine.new(flow, initial_state) }
126
123
  subject(:state) { engine.process([]) }
127
124
 
128
125
  it "should have added initial_state to state" do
129
126
  expect(subject.get(:key_1)).to eql 'a_state_object'
130
- expect(subject.get(:key_2)).to eql 'a_dynamic_state_object'
127
+ expect(subject.get(:key_2)).to eql :lambda
131
128
  end
132
129
  end
133
130