inform-runtime 1.2.2 → 1.3.1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -41
  3. data/lib/story_teller/articles.rb +2 -1
  4. data/lib/story_teller/builtins.rb +63 -10
  5. data/lib/story_teller/color.rb +2 -1
  6. data/lib/story_teller/command.rb +4 -1
  7. data/lib/story_teller/context.rb +23 -27
  8. data/lib/story_teller/daemon.rb +2 -2
  9. data/lib/story_teller/engine.rb +80 -16
  10. data/lib/story_teller/events.rb +9 -2
  11. data/lib/story_teller/experimental/handler_dsl.rb +1 -16
  12. data/lib/story_teller/experimental/reverse_engineer_class.rb +1 -1
  13. data/lib/story_teller/grammar_parser.rb +60 -13
  14. data/lib/story_teller/helpers.rb +8 -18
  15. data/lib/story_teller/history.rb +2 -1
  16. data/lib/story_teller/inflector.rb +4 -2
  17. data/lib/story_teller/inform/ephemeral/link.rb +63 -31
  18. data/lib/story_teller/inform/ephemeral/module.rb +6 -5
  19. data/lib/story_teller/inform/ephemeral/object.rb +223 -236
  20. data/lib/story_teller/inform/ephemeral/tag.rb +27 -14
  21. data/lib/story_teller/io.rb +99 -49
  22. data/lib/story_teller/kernel.rb +13 -5
  23. data/lib/story_teller/library/bootstrap.rb +10 -27
  24. data/lib/story_teller/library/declarations.rb +1 -1
  25. data/lib/story_teller/library/directives.rb +1 -1
  26. data/lib/story_teller/library/loader.rb +4 -20
  27. data/lib/story_teller/library/location.rb +9 -3
  28. data/lib/story_teller/library.rb +1 -1
  29. data/lib/story_teller/logging.rb +165 -67
  30. data/lib/story_teller/mixins.rb +11 -4
  31. data/lib/story_teller/plurals.rb +2 -1
  32. data/lib/story_teller/privileges.rb +117 -0
  33. data/lib/story_teller/prototype.rb +158 -32
  34. data/lib/story_teller/publication.rb +2 -1
  35. data/lib/story_teller/session.rb +115 -25
  36. data/lib/story_teller/stdlib.rb +232 -2
  37. data/lib/story_teller/subscription.rb +2 -1
  38. data/lib/story_teller/tree.rb +2 -1
  39. data/lib/story_teller/version.rb +2 -2
  40. data/lib/story_teller/world_tree.rb +21 -23
  41. data/lib/story_teller.rb +18 -5
  42. metadata +7 -10
  43. data/lib/story_teller/core.rb +0 -38
  44. data/lib/story_teller/ephemeral_adapter.rb +0 -40
  45. data/lib/story_teller/inform/base.rb +0 -160
  46. data/lib/story_teller/model_adapter.rb +0 -132
@@ -2,7 +2,7 @@
2
2
  # encoding: utf-8
3
3
  # frozen_string_literal: false
4
4
 
5
- # Copyright Nels Nelson 2008-2025 but freely usable (see license)
5
+ # Copyright Nels Nelson 2008-2026 but freely usable (see license)
6
6
  #
7
7
  # This file is part of StoryTeller.
8
8
  #
@@ -34,7 +34,9 @@ module Inform
34
34
  end
35
35
 
36
36
  def values
37
- super || instance_variables_map
37
+ result = super
38
+ return result if result && result != __method__
39
+ raise NoMethodError, "undefined method `#{__method__}' for #{self}"
38
40
  rescue StandardError => e
39
41
  log.warn "Unexpected error invoking super(Inform::Prototypical#values): #{e.message}"
40
42
  instance_variables_map
@@ -126,25 +128,44 @@ module Inform
126
128
  MethodWriterPattern = %r{=$}.freeze
127
129
  LinkPattern = %r{.+_to$}.freeze
128
130
 
131
+ # rubocop: disable Style/MissingRespondToMissing
132
+ def method_missing(method, *args, &block)
133
+ stack = Thread.current[:prototypical_method_missing_stack] ||= []
134
+ frame = [object_id, method]
135
+
136
+ if stack.include?(frame)
137
+ raise NoMethodError, "Aborting Prototypical missing method " \
138
+ "resolution #{self.class}##{method} on #{inspect}"
139
+ end
140
+
141
+ stack << frame
142
+ attempt_resolution(method, *args, &block)
143
+ ensure
144
+ stack&.pop
145
+ end
146
+ # rubocop: enable Style/MissingRespondToMissing
147
+
129
148
  # rubocop: disable Metrics/AbcSize
130
149
  # rubocop: disable Metrics/CyclomaticComplexity
131
150
  # rubocop: disable Metrics/MethodLength
132
151
  # rubocop: disable Metrics/PerceivedComplexity
133
- # rubocop: disable Style/MissingRespondToMissing
134
- def method_missing(method, *args, &block)
135
- return super if method == :to_ary
152
+ def attempt_resolution(method, *args, &block)
153
+ raise NoMethodError, "undefined method `#{method}' for #{self}" if method == :to_ary
136
154
  m = method.to_s
137
- return super if MethodBooleanOrBangPattern.match?(m)
155
+ raise NoMethodError, "undefined method `#{method}' for #{self}" if MethodBooleanOrBangPattern.match?(m)
138
156
  mname = MethodWriterPattern.match?(m) ? m.chop : m
139
157
  property = mname.to_sym
140
158
  variable = format(VariableTemplate, property: property).to_sym
141
159
  event = context_event
142
160
 
161
+ library = respond_to?(:inflib, true) ? inflib : nil
162
+ object_properties = respond_to?(:properties, true) ? properties : nil
163
+
143
164
  if MethodWriterPattern.match?(m) || (LinkPattern.match?(m) && !args.empty?)
144
165
  if !event.nil? && StoryTeller::Engine.invocation_properties.include?(property)
145
166
  event.send(method, *args, &block)
146
- elsif inflib&.library_method?(method)
147
- inflib.send(method, *args, &block)
167
+ elsif library&.library_method?(method)
168
+ library.send(method, *args, &block)
148
169
  else
149
170
  self.prototype(method, *args, &block)
150
171
  end
@@ -152,26 +173,33 @@ module Inform
152
173
  result = event.send(method, *args, &block)
153
174
  log.debug("Value for event #{event}.#{method} is: #{result || 'nil'}") if defined? DEBUG
154
175
  result
155
- elsif inflib&.library_method?(method)
156
- inflib.send(method, *args, &block)
157
- elsif inflib.respond_to?(method) && StoryTeller::Engine.invocation_properties.include?(property)
158
- inflib.send(method, *args, &block)
159
- elsif inflib&.instance_variable_get(variable) || inflib&.instance_variables&.include?(variable)
160
- inflib.instance_variable_get(variable)
161
- elsif (properties.respond_to?(:key?) && properties.key?(property)) ||
176
+ elsif library&.library_method?(method)
177
+ library.send(method, *args, &block)
178
+ elsif library.respond_to?(method) && StoryTeller::Engine.invocation_properties.include?(property)
179
+ library.send(method, *args, &block)
180
+ elsif library&.instance_variable_get(variable) || library&.instance_variables&.include?(variable)
181
+ library.instance_variable_get(variable)
182
+ elsif property_defined?(object_properties, property) ||
162
183
  (respond_to?(:_key?) && _key?(property)) ||
163
184
  !args.empty?
164
185
  self.prototype(method, *args, &block)
186
+ # TODO: Remove (2026-04-28)
187
+ # Experiment with supporting only property setting for those
188
+ # declared in StoryTeller::Library.declarations.properties.
189
+ # elsif (properties.respond_to?(:key?) && properties.key?(property)) ||
190
+ # (respond_to?(:_key?) && _key?(property)) ||
191
+ # StoryTeller::Library.declarations.properties.key?(property)
192
+ # self.prototype(method, *args, &block)
165
193
  else
166
- super
194
+ raise NoMethodError, "undefined method `#{method}' for #{self}"
167
195
  end
168
196
  end
169
197
  # rubocop: enable Metrics/AbcSize
170
198
  # rubocop: enable Metrics/CyclomaticComplexity
171
199
  # rubocop: enable Metrics/MethodLength
172
200
  # rubocop: enable Metrics/PerceivedComplexity
173
- # rubocop: enable Style/MissingRespondToMissing
174
201
 
202
+ # TODO: Remove (2026-04-28)
175
203
  # def respond_to_missing?(method, include_all=false)
176
204
  # m = method.to_s
177
205
  # return false if m =~ /\?$/
@@ -198,32 +226,81 @@ module Inform
198
226
  ).freeze
199
227
 
200
228
  # rubocop: disable Metrics/AbcSize
201
- # rubocop: disable Metrics/CyclomaticComplexity
202
229
  # rubocop: disable Metrics/MethodLength
203
230
  def prototype(key, *args, &block)
204
231
  ensure_properties_attribute!
205
232
  accessor, writer = accessor_and_writer(key)
233
+ link_result = nil
234
+
206
235
  if key_refers_to_link?(key)
207
236
  self.will_change_column(:properties) if self.respond_to?(:will_change_column)
208
- if args.empty? && self.properties.key?(key)
209
- return if (to_obj = get_constant(self.properties[key])).nil? || !to_obj.object?
210
- self.link(key, to_obj)
211
- self.properties.delete(key)
212
- self.save_changes if self.respond_to?(:save_changes)
213
- return to_obj
237
+
238
+ if args.empty?
239
+ link_result = resolve_link_property(key)
240
+ else
241
+ linked_object = object_for_link_value(args.first)
242
+ if linked_object.nil?
243
+ self.properties[key] = args.first
244
+ else
245
+ link_result = set_link_property(key, linked_object)
246
+ end
214
247
  end
215
- self.properties[key] = args.first
216
248
  end
249
+
217
250
  accessor = format(AccessorMethodTemplate, accessor: accessor)
218
251
  self.instance_eval(accessor, __FILE__, __LINE__)
219
252
  writer = format(WriterMethodTemplate, writer: writer)
220
253
  self.instance_eval(writer, __FILE__, __LINE__)
254
+
255
+ return link_result unless link_result.nil?
256
+
221
257
  self.send(key, *args, &block)
222
258
  end
223
259
  # rubocop: enable Metrics/AbcSize
224
- # rubocop: enable Metrics/CyclomaticComplexity
225
260
  # rubocop: enable Metrics/MethodLength
226
261
 
262
+ # rubocop: disable Metrics/CyclomaticComplexity
263
+ def resolve_link_property(key)
264
+ return nil unless key_refers_to_link?(key)
265
+
266
+ linked_object = self._get_object(key) if self.respond_to?(:_key?) &&
267
+ self._key?(key) &&
268
+ self.respond_to?(:_get_object)
269
+ return linked_object unless linked_object.nil?
270
+
271
+ property_key = property_key_for(key)
272
+ return nil if property_key.nil?
273
+
274
+ linked_object = object_for_link_value(self.properties[property_key])
275
+ return nil if linked_object.nil?
276
+
277
+ set_link_property(key, linked_object, property_key: property_key)
278
+ end
279
+ # rubocop: enable Metrics/CyclomaticComplexity
280
+
281
+ def object_for_link_value(value)
282
+ return value if value.respond_to?(:object?) && value.object?
283
+ return nil unless value.is_a?(Symbol) || value.is_a?(String)
284
+
285
+ object = constant_for_link_value(value)
286
+ return object if object.respond_to?(:object?) && object.object?
287
+
288
+ nil
289
+ end
290
+
291
+ def constant_for_link_value(value)
292
+ names = value.to_s.split('::').reject(&:empty?)
293
+ return nil if names.empty?
294
+
295
+ names.inject(::Object) do |scope, name|
296
+ return nil unless scope.const_defined?(name)
297
+
298
+ scope.const_get(name)
299
+ end
300
+ rescue NameError
301
+ nil
302
+ end
303
+
227
304
  MISSING_PROPERTIES_MESSAGE = 'Missing properties attribute for: ' \
228
305
  '%<identity>s'.freeze
229
306
 
@@ -232,14 +309,46 @@ module Inform
232
309
  raise StandardError, format(MISSING_PROPERTIES_MESSAGE, identity: self.identity)
233
310
  end
234
311
 
312
+ def property_defined?(object_properties, property)
313
+ return false unless object_properties.respond_to?(:key?)
314
+
315
+ object_properties.key?(property) || object_properties.key?(property.to_s)
316
+ end
317
+
235
318
  def key_refers_to_link?(key)
236
- StoryTeller::Library.declarations.properties.key?(key) && LinkPattern.match?(key.to_s)
319
+ return false unless LinkPattern.match?(key.to_s)
320
+
321
+ declared_property?(key)
322
+ end
323
+
324
+ def declared_property?(key)
325
+ return false unless defined?(StoryTeller::Library)
326
+ return false unless StoryTeller::Library.respond_to?(:declarations)
327
+
328
+ declarations = StoryTeller::Library.declarations
329
+ properties = declarations.properties if declarations.respond_to?(:properties)
330
+ return false unless properties.respond_to?(:key?)
331
+
332
+ properties.key?(key.to_sym) || properties.key?(key.to_s)
333
+ rescue StandardError
334
+ false
335
+ end
336
+
337
+ def property_key_for(key)
338
+ return key if self.properties.key?(key)
339
+
340
+ string_key = key.to_s
341
+ return string_key if self.properties.key?(string_key)
342
+
343
+ nil
237
344
  end
238
345
 
239
346
  def _get(key)
240
347
  result = nil
241
348
  result = self._get_object(key) if self.respond_to?(:_key?) && self._key?(key)
242
- result || self.properties.dup[key]
349
+ return result unless result.nil?
350
+
351
+ resolve_link_property(key) || self.properties.dup[property_key_for(key)]
243
352
  end
244
353
 
245
354
  # rubocop: disable Metrics/AbcSize
@@ -252,11 +361,13 @@ module Inform
252
361
  result = nil
253
362
  if values.first.object?
254
363
  result = self._set_object(key, values.first) if respond_to? :_set_object
364
+ elsif key_refers_to_link?(key) && (linked_object = object_for_link_value(values.first))
365
+ result = set_link_property(key, linked_object)
255
366
  elsif values.first.nil? && self.respond_to?(:_key?) && self._key?(key)
256
367
  result = self._unset_object(key) if self.respond_to?(:_unset_object)
257
368
  elsif values.first.nil?
258
369
  self.will_change_column(:properties) if self.respond_to?(:will_change_column)
259
- self.properties.delete(key)
370
+ self.properties.delete(property_key_for(key))
260
371
  self.save_changes if self.respond_to?(:save_changes)
261
372
  elsif values.length > 1
262
373
  self.will_change_column(:properties) if self.respond_to?(:will_change_column)
@@ -269,7 +380,7 @@ module Inform
269
380
  self.properties[key] = values.first
270
381
  self.save_changes if self.respond_to?(:save_changes)
271
382
  end
272
- result || self.properties.dup[key]
383
+ result || self.properties.dup[property_key_for(key)]
273
384
  end
274
385
  # rubocop: enable Metrics/AbcSize
275
386
  # rubocop: enable Metrics/CyclomaticComplexity
@@ -285,23 +396,38 @@ module Inform
285
396
  result = nil
286
397
  if value.object?
287
398
  result = self._set_object(key, value) if self.respond_to?(:_set_object)
399
+ elsif key_refers_to_link?(key) && (linked_object = object_for_link_value(value))
400
+ result = set_link_property(key, linked_object)
288
401
  elsif value.nil? && self.respond_to?(:_key?) && self._key?(key)
289
402
  result = self._unset_object(key) if self.respond_to?(:_unset_object)
290
403
  elsif value.nil? && self.properties.key?(key)
291
404
  self.will_change_column(:properties) if self.respond_to?(:will_change_column)
292
- self.properties.delete(key)
405
+ self.properties.delete(property_key_for(key))
293
406
  self.save_changes if self.respond_to?(:save_changes)
294
407
  else
295
408
  self.will_change_column(:properties) if self.respond_to?(:will_change_column)
296
409
  self.properties[key] = value
297
410
  self.save_changes if self.respond_to?(:save_changes)
298
411
  end
299
- result || self.properties.dup[key]
412
+ result || self.properties.dup[property_key_for(key)]
300
413
  end
301
414
  # rubocop: enable Metrics/AbcSize
302
415
  # rubocop: enable Metrics/CyclomaticComplexity
303
416
  # rubocop: enable Metrics/MethodLength
304
417
  # rubocop: enable Metrics/PerceivedComplexity
418
+
419
+ def set_link_property(key, object, property_key: property_key_for(key))
420
+ result = if self.respond_to?(:_set_object)
421
+ self._set_object(key, object)
422
+ elsif self.respond_to?(:link)
423
+ self.link(key, object)
424
+ object
425
+ end
426
+ self.properties.delete(property_key) unless property_key.nil?
427
+ self.will_change_column(:properties) if self.respond_to?(:will_change_column)
428
+ self.save_changes if self.respond_to?(:save_changes)
429
+ result || object
430
+ end
305
431
  end
306
432
  # module Prototypical
307
433
  end
@@ -1,7 +1,8 @@
1
+ # lib/story_teller/publication.rb
1
2
  # encoding: utf-8
2
3
  # frozen_string_literal: false
3
4
 
4
- # Copyright Nels Nelson 2008-2025 but freely usable (see license)
5
+ # Copyright Nels Nelson 2008-2026 but freely usable (see license)
5
6
  #
6
7
  # This file is part of the StoryTeller.
7
8
  #
@@ -1,7 +1,8 @@
1
+ # lib/story_teller/session.rb
1
2
  # encoding: utf-8
2
3
  # frozen_string_literal: false
3
4
 
4
- # Copyright Nels Nelson 2008-2025 but freely usable (see license)
5
+ # Copyright Nels Nelson 2008-2026 but freely usable (see license)
5
6
  #
6
7
  # This file is part of StoryTeller.
7
8
  #
@@ -22,6 +23,7 @@
22
23
  module SessionManagementMethods
23
24
  class Registry < Hash; end
24
25
  SessionsByChannel = Registry.new
26
+ SessionsByPlayer = Registry.new
25
27
 
26
28
  def add_promiscuous_states(*states)
27
29
  Promiscuous.merge states.flatten
@@ -31,12 +33,52 @@ module SessionManagementMethods
31
33
  SessionsByChannel
32
34
  end
33
35
 
36
+ def sessions_by_player
37
+ SessionsByPlayer
38
+ end
39
+
34
40
  def register(channel, session)
35
41
  sessions_by_channel[channel] = session
42
+ session.channel = channel if session.respond_to?(:channel=)
43
+ session
44
+ end
45
+
46
+ def register_player(player, session)
47
+ sessions_by_player[player] = session
48
+ session
36
49
  end
37
50
 
38
51
  def unregister(channel)
39
- sessions_by_channel.delete(channel)
52
+ session = sessions_by_channel.delete(channel)
53
+ sessions_by_player.delete_if { |_player, candidate| candidate.equal?(session) }
54
+ session
55
+ end
56
+
57
+ def release_player(player, session = nil)
58
+ return nil if player.nil?
59
+ return sessions_by_player.delete(player) if session.nil?
60
+ return sessions_by_player.delete(player) if sessions_by_player[player].equal?(session)
61
+
62
+ nil
63
+ end
64
+
65
+ def of(obj)
66
+ return nil if obj.nil?
67
+ return obj if obj.is_a?(self)
68
+
69
+ sessions_by_channel[obj] ||
70
+ sessions_by_player[obj] ||
71
+ session_for_method(obj, :player) ||
72
+ session_for_method(obj, :selfobj) ||
73
+ session_for_ivar(obj, :@player)
74
+ end
75
+
76
+ def players
77
+ sessions_by_player.keys
78
+ end
79
+
80
+ def channels
81
+ sessions_by_channel.keys
40
82
  end
41
83
 
42
84
  def [](channel)
@@ -52,7 +94,21 @@ module SessionManagementMethods
52
94
  end
53
95
 
54
96
  def get(channel)
55
- sessions_by_channel[channel] ||= Session.new(channel)
97
+ sessions_by_channel[channel] ||= new(channel)
98
+ end
99
+
100
+ private
101
+
102
+ def session_for_method(obj, method_name)
103
+ return nil unless obj.respond_to?(method_name)
104
+
105
+ sessions_by_player[obj.public_send(method_name)]
106
+ end
107
+
108
+ def session_for_ivar(obj, ivar_name)
109
+ return nil unless obj.instance_variable_defined?(ivar_name)
110
+
111
+ sessions_by_player[obj.instance_variable_get(ivar_name)]
56
112
  end
57
113
  end
58
114
  # module SessionManagementMethods
@@ -84,28 +140,43 @@ module SessionStateManagementMethods
84
140
  end
85
141
  end
86
142
 
87
- module Inform
143
+ # module StoryTeller
144
+ module StoryTeller
145
+ # module IO
88
146
  module IO
89
147
  # The Session class
90
148
  # TODO: Refactor method implementations into modules
149
+ # rubocop: disable Metrics/ClassLength
91
150
  class Session
92
- # These states accept any input, including no input
93
- Promiscuous = Set.new
94
151
  include SessionStateManagementMethods
95
152
 
96
153
  class << self
97
154
  include SessionManagementMethods
98
155
  end
99
- attr_accessor :channel, :state, :status
156
+ attr_accessor :channel, :machine, :player, :state, :status
100
157
  attr_reader :last_good_state, :last_activity, :previous, :inbound, :outbound, :settings
101
158
 
102
- def initialize(channel = nil)
103
- init_channel_session(channel) unless channel.nil?
104
- @status = nil
105
- @last_good_state = @state = default if respond_to? :default
159
+ # These states accept any input, including no input
160
+ Promiscuous = Set.new
161
+
162
+ # rubocop: disable Metrics/MethodLength
163
+ def initialize(channel = nil, machine: nil, player: nil, state: :playing, settings: {})
164
+ @channel = channel
165
+ @machine = machine
166
+ @player = nil
167
+ @state = state
168
+ @last_good_state = state
106
169
  @session_data = {}
107
- @settings = {}
170
+ @settings = settings
171
+ @status = nil
172
+ @last_activity = Time.now
173
+
174
+ self.class.register(channel, self) unless channel.nil?
175
+ control(player) unless player.nil?
108
176
  end
177
+ # rubocop: enable Metrics/MethodLength
178
+
179
+ alias preferences settings
109
180
 
110
181
  def init_channel_session(channel)
111
182
  Session[channel] = self
@@ -115,6 +186,13 @@ class Session
115
186
  @last_activity = Time.now
116
187
  end
117
188
 
189
+ def control(player)
190
+ self.class.release_player(@player, self)
191
+ @player = player
192
+ self.class.register_player(player, self) unless player.nil?
193
+ self
194
+ end
195
+
118
196
  def receive(message)
119
197
  return if message.nil? # It is okay if the message parameter is an empty string
120
198
  return disconnected if @channel.nil?
@@ -122,7 +200,7 @@ class Session
122
200
  process(sanitize(message))
123
201
  end
124
202
 
125
- def update(machine = self)
203
+ def update(machine = @machine)
126
204
  safely_progress(machine)
127
205
  after_update if self.respond_to?(:after_update)
128
206
  @last_good_state = @state unless @state == :confused
@@ -140,24 +218,27 @@ class Session
140
218
  self
141
219
  end
142
220
 
221
+ def expose_to(machine)
222
+ machine.instance_variable_set(:@buffer, @buffer)
223
+ machine.instance_variable_set(:@session, self)
224
+ end
225
+
143
226
  ExitCommandPattern = /^(exit|quit|q)$/i.freeze
144
227
 
145
228
  def exit_command?(message)
146
229
  !Promiscuous.include?(@state) && ExitCommandPattern.match?(message)
147
230
  end
148
231
 
149
- def process(message)
150
- return disconnect if exit_command?(message)
151
- @buffer = message
232
+ def process(message, machine = @machine)
233
+ raise ArgumentError, 'session machine is required' if machine.nil?
234
+
235
+ @machine = machine
236
+ @buffer = sanitize(message)
152
237
  @last_activity = Time.now
153
238
  @status = :active
154
- if valid_state?
155
- update
156
- else
157
- @inbound.publish @buffer
158
- end
159
239
 
160
- respond
240
+ expose_to(machine)
241
+ update(machine)
161
242
  end
162
243
 
163
244
  def respond
@@ -165,7 +246,7 @@ class Session
165
246
  end
166
247
 
167
248
  def safely_progress(machine = self)
168
- @state = machine.send(@state)
249
+ @state = machine.public_send(@state)
169
250
  raise 'Bad state machine implementation: nil state' if @state.nil?
170
251
  rescue StandardError => e
171
252
  log.error "Error updating state: #{e.message}", e
@@ -176,7 +257,7 @@ class Session
176
257
  def valid_state?(machine = self)
177
258
  case @state
178
259
  when String, Symbol
179
- machine.respond_to? @state
260
+ machine.respond_to?(@state)
180
261
  else
181
262
  false
182
263
  end
@@ -189,12 +270,21 @@ class Session
189
270
  prompt
190
271
  @last_good_state
191
272
  end
273
+
274
+ private
275
+
276
+ def prepare_machine(machine)
277
+ return if machine.equal?(self)
278
+
279
+ machine.instance_variable_set(:@buffer, @buffer)
280
+ end
192
281
  end
193
282
  # class Session
283
+ # rubocop: enable Metrics/ClassLength
194
284
 
195
285
  # The Connection class
196
286
  class Connection; end
197
287
  end
198
288
  # module IO
199
289
  end
200
- # module Inform
290
+ # module StoryTeller