Class: James::State

Inherits:
Object
  • Object
show all
Defined in:
lib/james/state_api.rb,
lib/james/state_internals.rb

Overview

A state is defined in a dialog.

It has a name with which it can be targeted.

A state has three methods:

* hear: If this phrase (or one of these phrases) is heard, move to that state. Takes a hash.
* into: A block that is called on entering.
* exit: A block that is called on exit.

Example:

state :time do
  hear ['What time is it?', 'And now?'] => :time
  into { time = Time.now; "It is currently #{time.hour} #{time.min}." }
  exit { "And that was the time." }
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, context) ⇒ State

Returns a new instance of State.



23
24
25
26
27
28
29
30
# File 'lib/james/state_api.rb', line 23

def initialize name, context
  @name    = name
  @context = context

  @transitions = {}

  instance_eval(&Proc.new) if block_given?
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



21
22
23
# File 'lib/james/state_api.rb', line 21

def context
  @context
end

#nameObject (readonly)

Returns the value of attribute name.



21
22
23
# File 'lib/james/state_api.rb', line 21

def name
  @name
end

Instance Method Details

#<<(dialog) ⇒ Object

Chain the given dialog to this state.



75
76
77
# File 'lib/james/state_api.rb', line 75

def << dialog
  dialog.chain_to self
end

#__exit__Object

Called by the visitor visiting this state.



47
48
49
# File 'lib/james/state_internals.rb', line 47

def __exit__
  @exit_block && context.instance_eval(&@exit_block)
end

#__into__Object

Called by the visitor visiting this state.



41
42
43
# File 'lib/james/state_internals.rb', line 41

def __into__
  @into_block && context.instance_eval(&@into_block)
end

#__transition__(&block) ⇒ Object

Called by the visitor visiting this state.



53
54
55
# File 'lib/james/state_internals.rb', line 53

def __transition__ &block
  context.instance_eval &block
end

#chainableObject

A chainable state is a state from which other Dialogs can be reached.



82
83
84
# File 'lib/james/state_api.rb', line 82

def chainable
  @chainable = true
end

#chainable?Boolean

By default, a state is not chainable.

Returns:

  • (Boolean)


117
118
119
# File 'lib/james/state_api.rb', line 117

def chainable?
  !!@chainable
end

#exit(text = nil, &block) ⇒ Object

Execute this block or say the text when exiting this state.

Examples:

exit "Yes, Sir?"
exit { "A random number is #{rand(10)}" }


67
68
69
70
71
# File 'lib/james/state_api.rb', line 67

def exit text = nil, &block
  @exit_block = block ||
                text && lambda { text } ||
                raise_no_text_or_block(__method__)
end

#expand(transitions) ⇒ Object

Expands a hash in the form

* [a, b] => c to a => c, b => c

but leaves a non-array key alone.



99
100
101
102
103
104
105
106
107
# File 'lib/james/state_api.rb', line 99

def expand transitions
  results = {}
  transitions.each_pair do |phrases, state_name|
    [*phrases].each do |phrase|
      results[phrase] = state_name
    end
  end
  results
end

#expectsObject



13
14
15
# File 'lib/james/state_internals.rb', line 13

def expects
  transitions.keys
end

#hear(transitions) ⇒ Object

How do I get from this state to another?

Example:

hear 'What time is it?' => :time,
     'What? This late?' => :yes

Example for staying in the same state:

hear 'What time is it?' # Implicitly staying.

Example for staying in the same state and doing something:

hear 'What time is it?' => ->() { "I'm staying in the same state" }


44
45
46
47
# File 'lib/james/state_api.rb', line 44

def hear transitions
  transitions = { transitions => name } unless transitions.respond_to?(:to_hash)
  @transitions = expand(transitions).merge @transitions
end

#internal_expectsObject



19
20
21
# File 'lib/james/state_internals.rb', line 19

def internal_expects
  transitions.select { |phrase, target| target.respond_to?(:to_sym) || target == self.context }.keys
end

#into(text = nil, &block) ⇒ Object

Execute this block or say the text when entering this state.

Examples:

into "Yes, Sir?"
into { "A random number is #{rand(10)}" }


55
56
57
58
59
# File 'lib/james/state_api.rb', line 55

def into text = nil, &block
  @into_block = block ||
                text && lambda { text } ||
                raise_no_text_or_block(__method__)
end

#next_for(phrase) ⇒ Object

Returns the next state for the given phrase.

It accesses the context (aka Dialog) to get a full object state.

If it is a Symbol, James will try to get the real state. If not, it will just return it (a State already, or lambda).



31
32
33
34
# File 'lib/james/state_internals.rb', line 31

def next_for phrase
  state = self.transitions[phrase]
  state.respond_to?(:id2name) ? context.state_for(state) : state
end

#raise_no_text_or_block(on_method) ⇒ Object

Raise an ArgumentError for the given method if it needs either a text or a block.

Raises:

  • (ArgumentError)


111
112
113
# File 'lib/james/state_api.rb', line 111

def raise_no_text_or_block on_method
  raise ArgumentError.new("Neither block nor text given to ##{on_method} call in #{caller[1]}.")
end

#to_sObject

Description of self using name and transitions.



88
89
90
# File 'lib/james/state_api.rb', line 88

def to_s
  "#{self.class.name}(#{name}, #{context}, #{@transitions})"
end

#transitionsObject

Transitions are internal transitions & external transitions.



7
8
9
# File 'lib/james/state_internals.rb', line 7

def transitions
  @transitions
end