smartdown 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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