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
@@ -1,7 +1,8 @@
1
+ # lib/story_teller/io.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
  #
@@ -23,8 +24,10 @@ module StoryTeller
23
24
  # The IO module
24
25
  module IO
25
26
  def session
26
- return unless defined?(StoryTeller::IO::Session) && StoryTeller::IO::Session.respond_to?(:of)
27
- @session ||= StoryTeller::IO::Session.of(self)
27
+ return @session if defined?(@session) && !@session.nil?
28
+ return nil unless defined?(StoryTeller::IO::Session)
29
+
30
+ StoryTeller::IO::Session.of(self)
28
31
  end
29
32
 
30
33
  def noop; end
@@ -98,76 +101,123 @@ module StoryTeller
98
101
  out(@prompt = s || "\n>")
99
102
  end
100
103
 
104
+ def preference_value(key)
105
+ prefs = session.preferences
106
+
107
+ return prefs.public_send(key) if prefs.respond_to?(key)
108
+ return prefs[key] if prefs.respond_to?(:[]) && prefs.key?(key)
109
+ return prefs[key.to_s] if prefs.respond_to?(:[]) && prefs.key?(key.to_s)
110
+
111
+ nil
112
+ end
113
+
101
114
  # ----------------------------------------------------------------------------
102
115
  # Adapted from the C code for ls. :-)
103
116
  # ----------------------------------------------------------------------------
104
117
 
105
118
  ItemPaddingString = '%<item>s%<padding>s'.freeze
119
+ DefaultLineLength = 30
106
120
  MinimumLineLength = 30
107
121
  MinimumCellPadding = 8
108
122
  MinimumColumnPadding = 2
109
123
 
124
+ def preferred_line_length
125
+ width = preference_value(:word_wrap).to_i
126
+ return width if width > MinimumLineLength
127
+
128
+ DefaultLineLength
129
+ end
130
+
131
+ # def many_per_line(items)
132
+ # max = 0
133
+
134
+ # lines = []
135
+ # prefs = @player.linkto :preferences
136
+ # line_length = preferred_line_length
137
+ # buffer = ''
138
+
139
+ # items.each do |item|
140
+ # max = [item.length, max].max
141
+ # end
142
+ # max += MinimumColumnPadding
143
+ # width = (line_length - MinimumCellPadding) / max
144
+ # height = (items.length / width).ceil - 1
145
+
146
+ # return if width == 0
147
+
148
+ # columns = []
149
+ # i = 0
150
+ # loop do
151
+ # # Build a list of columns with nil padding
152
+ # column = items[i, height]
153
+ # columns << column + Array.new(height - column.length)
154
+ # i += height
155
+ # break if (i + height) >= (items.length + height)
156
+ # end
157
+
158
+ # m = []
159
+ # loop do
160
+ # m << []
161
+ # for column in columns
162
+ # m.last << column.shift
163
+ # end
164
+ # break if columns.last.empty?
165
+ # end
166
+
167
+ # m.each do |row|
168
+ # buffer = "\e[51m"
169
+ # row.each do |item|
170
+ # next unless item
171
+ # n = [max - item.length, MinimumColumnPadding].max
172
+ # padding = " " * n unless item == row.last
173
+ # buffer += format(ItemPaddingString, item: item, padding: padding)
174
+ # end
175
+ # buffer += "\e[56m"
176
+ # lines << buffer.strip
177
+ # buffer = ''
178
+ # end
179
+
180
+ # lines
181
+ # end
182
+ # # def many_per_line
183
+
110
184
  # rubocop: disable Metrics/AbcSize
111
185
  # rubocop: disable Metrics/CyclomaticComplexity
112
186
  # rubocop: disable Metrics/MethodLength
113
- # rubocop: disable Metrics/PerceivedComplexity
114
187
  def many_per_line(items)
115
- max = 0
188
+ items = items.map(&:to_s)
189
+ return [] if items.empty?
116
190
 
117
- lines = []
118
- prefs = @player.linkto :preferences
119
- line_length = prefs ? prefs.word_wrap.to_i : MinimumLineLength
120
- line_length = MinimumLineLength if line_length <= MinimumLineLength
121
- buffer = ''
191
+ line_length = preferred_line_length
192
+ max_item_length = items.map(&:length).max || 0
193
+ cell_width = max_item_length + MinimumColumnPadding
122
194
 
123
- items.each do |item|
124
- max = [item.length, max].max
125
- end
126
- max += MinimumColumnPadding
127
- width = (line_length - MinimumCellPadding) / max
128
- height = (items.length / width).ceil - 1
129
-
130
- return if width == 0
131
-
132
- columns = []
133
- i = 0
134
- loop do
135
- # Build a list of columns with nil padding
136
- column = items[i, height]
137
- columns << column + Array.new(height - column.length)
138
- i += height
139
- break if (i + height) >= (items.length + height)
140
- end
195
+ column_count = (line_length - MinimumCellPadding) / cell_width
196
+ column_count = 1 if column_count < 1
197
+ column_count = items.length if column_count > items.length
141
198
 
142
- m = []
143
- loop do
144
- m << []
145
- for column in columns
146
- m.last << column.shift
147
- end
148
- break if columns.last.empty?
149
- end
199
+ row_count = (items.length.to_f / column_count).ceil
200
+ rows = []
201
+
202
+ row_count.times do |row_index|
203
+ row = []
150
204
 
151
- m.each do |row|
152
- buffer = "\e[51m"
153
- row.each do |item|
154
- next unless item
155
- n = [max - item.length, MinimumColumnPadding].max
156
- padding = " " * n unless item == row.last
157
- buffer += format(ItemPaddingString, item: item, padding: padding)
205
+ column_count.times do |column_index|
206
+ item_index = row_index + (column_index * row_count)
207
+ item = items[item_index]
208
+ next if item.nil?
209
+
210
+ row << item.ljust(cell_width)
158
211
  end
159
- buffer += "\e[56m"
160
- lines << buffer.strip
161
- buffer = ''
212
+
213
+ rows << row.join.rstrip
162
214
  end
163
215
 
164
- lines
216
+ rows
165
217
  end
166
218
  # rubocop: enable Metrics/AbcSize
167
219
  # rubocop: enable Metrics/CyclomaticComplexity
168
220
  # rubocop: enable Metrics/MethodLength
169
- # rubocop: enable Metrics/PerceivedComplexity
170
- # def many_per_line
171
221
  end
172
222
  # module IO
173
223
  end
@@ -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
  #
@@ -176,17 +176,23 @@ module Kernel
176
176
 
177
177
  raise MethodAlreadyDefined, :class_loader if respond_to?(:class_loader)
178
178
  def class_loader
179
- JRuby.runtime.jruby_class_loader
179
+ return JRuby.runtime.jruby_class_loader if defined?(JRuby)
180
+
181
+ raise NotImplementedError, "class_loader is only available on JRuby"
180
182
  end
181
183
 
182
184
  raise MethodAlreadyDefined, :load_service if respond_to?(:load_service)
183
185
  def load_service
184
- JRuby.runtime.load_service
186
+ return JRuby.runtime.load_service if defined?(JRuby)
187
+
188
+ raise NotImplementedError, "load_service is only available on JRuby"
185
189
  end
186
190
 
187
191
  raise MethodAlreadyDefined, :object_space if respond_to?(:object_space)
188
192
  def object_space
189
- JRuby.runtime.object_space
193
+ return JRuby.runtime.object_space if defined?(JRuby)
194
+
195
+ raise NotImplementedError, "object_space is only available on JRuby"
190
196
  end
191
197
 
192
198
  JavaPattern = %r{java}.freeze
@@ -205,6 +211,8 @@ module Kernel
205
211
  raise MethodAlreadyDefined, :collect_garbage if respond_to?(:collect_garbage)
206
212
  # rubocop: disable Metrics/AbcSize
207
213
  def collect_garbage
214
+ return unless java?
215
+
208
216
  java.lang.System.gc()
209
217
  sleep 0.1
210
218
  java.lang.System.runFinalization()
@@ -338,7 +346,7 @@ remove_method :original_rand
338
346
  return 0 if obj.nil?
339
347
  obj.refresh if obj.respond_to?(:refresh)
340
348
  if defined? db
341
- return db.run('SELECT count(id) from "object" where parent_id = ' + obj.id) unless sysobj?
349
+ return db.run('SELECT count(id) from "object" where parent_id = ' + obj.id) unless ephemeral?
342
350
  end
343
351
  return obj.instance_variable_get(prop).length if obj.instance_variable_defined?(prop)
344
352
  return unless obj.respond_to?(:children)
@@ -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 the StoryTeller.
8
8
  #
@@ -27,37 +27,20 @@ module StoryTeller
27
27
  module Bootstrap
28
28
  module_function
29
29
 
30
- def ensure_location(inflib)
31
- return if inflib.nil?
32
- return if inflib.location
30
+ def manage_inform_library(player: nil)
31
+ existing = StoryTeller::Engine.libraries[player] unless player.nil?
32
+ return existing unless existing.nil?
33
33
 
34
- player_obj = inflib.player
35
- location = player_obj&.location
36
- location ||= player_obj&.spawn_point if player_obj.respond_to?(:spawn_point)
37
- inflib.instance_variable_set(:@location, location)
34
+ new_inform_library(player:)
38
35
  end
39
36
 
40
- def set_selfobj(inflib, player)
41
- inflib.selfobj = player unless player.nil?
42
- end
43
-
44
- # rubocop: disable Metrics/MethodLength
45
- def manage_inform_library(player: nil, key: StoryTeller::Engine, &block)
46
- inflib = StoryTeller::Engine.libraries[key]
47
- unless inflib.nil? || player.nil?
48
- set_selfobj(inflib, player)
49
- return inflib
50
- end
51
-
52
- inflib = block_given? ? block.call : InformLibrary.new
53
- return nil if inflib.nil?
54
-
55
- set_selfobj(inflib, player)
37
+ def new_inform_library(player: nil)
38
+ inflib = InformLibrary.new
39
+ bind_inform_library(inflib, player)
56
40
  inflib.subscribe(inflib.selfobj)
57
- ensure_location(inflib)
58
- StoryTeller::Engine.libraries[key] = inflib
41
+ ensure_player_location(inflib) if StoryTeller::Engine.persist_player_location?
42
+ inflib
59
43
  end
60
- # rubocop: enable Metrics/MethodLength
61
44
  end
62
45
  # module Bootstrap
63
46
  end
@@ -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 the StoryTeller.
8
8
  #
@@ -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 the StoryTeller.
8
8
  #
@@ -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 the StoryTeller.
8
8
  #
@@ -33,32 +33,18 @@ module StoryTeller
33
33
  module_function
34
34
 
35
35
  def augment_inform_library!
36
- ensure_inform_object_model!
37
36
  InformLibrary.extend(StoryTeller::Engine::Library)
38
37
  InformLibrary.class_eval do
39
38
  include Inform::Context unless include?(Inform::Context)
40
39
  include Inform::StdLib unless include?(Inform::StdLib)
40
+ include Inform::Rules unless include?(Inform::Rules)
41
+ include Inform::History unless include?(Inform::History)
41
42
  end
42
43
  InformLibrary.prepend(StoryTeller::Library::Location)
43
44
  end
44
45
 
45
46
  def load_library
46
47
  augment_inform_library!
47
- StoryTeller::Library::Bootstrap.manage_inform_library
48
- end
49
-
50
- def inform_object_model?
51
- object_class = StoryTeller::ModelAdapter.object_class
52
- return false if object_class.nil?
53
-
54
- object_class.method_defined?(:<<) &&
55
- object_class.method_defined?(:with)
56
- end
57
-
58
- def ensure_inform_object_model!
59
- return if inform_object_model?
60
-
61
- StoryTeller::EphemeralAdapter.register!
62
48
  end
63
49
 
64
50
  # rubocop: disable Metrics/AbcSize
@@ -84,6 +70,7 @@ module StoryTeller
84
70
  end
85
71
  # rubocop: enable Metrics/AbcSize
86
72
  # rubocop: enable Metrics/MethodLength
73
+
87
74
  def load_grammar(module_name)
88
75
  Inform::Grammar.load_by_name(module_name)
89
76
  end
@@ -97,8 +84,5 @@ module StoryTeller
97
84
  Inform::Grammar::Verbs.clear
98
85
  end
99
86
  end
100
- # module Loader
101
87
  end
102
- # module Library
103
88
  end
104
- # module StoryTeller
@@ -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 the StoryTeller.
8
8
  #
@@ -26,11 +26,17 @@ module StoryTeller
26
26
  # module Location
27
27
  module Location
28
28
  def location
29
- @player&.location
29
+ return @location unless StoryTeller::Engine.persist_player_location?
30
+
31
+ @player&.location || @location
30
32
  end
31
33
 
32
34
  def location=(obj)
33
- @player.location = obj
35
+ if StoryTeller::Engine.persist_player_location?
36
+ @player.location = obj
37
+ else
38
+ @location = obj
39
+ end
34
40
  end
35
41
  end
36
42
 
@@ -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 the StoryTeller.
8
8
  #