inform-runtime 1.2.3 → 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 +2 -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 +45 -10
  10. data/lib/story_teller/events.rb +1 -1
  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 -4
  15. data/lib/story_teller/history.rb +1 -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 +2 -2
  23. data/lib/story_teller/library/bootstrap.rb +2 -2
  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 +3 -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 +1 -1
  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 +89 -6
  33. data/lib/story_teller/prototype.rb +150 -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 +231 -1
  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 +6 -10
  43. data/lib/story_teller/core.rb +0 -39
  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,8 +1,8 @@
1
- # lib/story_teller/object.rb
1
+ # lib/story_teller/inform/object.rb
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
  #
@@ -13,11 +13,11 @@
13
13
  #
14
14
  # The StoryTeller is distributed in the hope that it will be useful,
15
15
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
17
  # GNU General Public License for more details.
18
18
  #
19
19
  # You should have received a copy of the GNU General Public License
20
- # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
20
+ # along with the StoryTeller. If not, see <http://www.gnu.org/licenses/>.
21
21
 
22
22
  # Object
23
23
 
@@ -28,7 +28,7 @@
28
28
  # obj
29
29
  # end
30
30
 
31
- def Object(name, klass = Inform::Object, &block)
31
+ def Object(name, klass = Inform::Ephemeral::Object, &block)
32
32
  obj = if klass.respond_to?(:fetch_or_create_by_name)
33
33
  klass.fetch_or_create_by_name(name)
34
34
  else
@@ -39,291 +39,278 @@ def Object(name, klass = Inform::Object, &block)
39
39
  obj
40
40
  end
41
41
 
42
- # The Inform module
42
+ # module Inform
43
43
  module Inform
44
- # module Ephemeral
45
- module Ephemeral
46
- # The Inform::Ephemeral::Object class
47
- # rubocop: disable Metrics/ClassLength
48
- class Object < Inform::Object
49
- Instances = Struct.new(:memo).new(defined?(Java) ? java.util.concurrent.CopyOnWriteArrayList.new : [])
50
- attr_accessor :short_name, :display_name, :inflib, :properties, :links,
51
- :modules, :tags, :created_at, :modified_at, :visited
52
-
53
- attr_writer :description
54
- attr_reader :original_description_method
55
-
56
- # rubocop: disable Metrics/AbcSize
57
- # rubocop: disable Metrics/MethodLength
58
- def initialize(textual_name = nil, *args, &block)
59
- super(*args)
60
- @name = textual_name
61
- @short_name = textual_name || format('(%<short_name>s)', short_name: self.class.name)
62
- @display_name = textual_name
63
- @description = nil
64
- @adjectives = []
65
- @properties = {}
66
- @links = []
67
- @modules = []
68
- @tags = []
69
- @children = []
70
- @visited = []
71
- @created_at = @modified_at = Time.now
72
- index_words if respond_to?(:index_words)
73
- index_obj(self)
74
- @original_description_method = self.class.instance_method(:description)
75
- self.with(&block) if block_given?
76
- end
77
- # rubocop: enable Metrics/AbcSize
78
- # rubocop: enable Metrics/MethodLength
44
+ # module Ephemeral
45
+ module Ephemeral
46
+ # class Object
47
+ # rubocop: disable Metrics/ClassLength
48
+ class Object
49
+ DefaultShortNameTemplate = '(%<short_name>s)'.freeze
50
+
51
+ attr_accessor :short_name, :display_name, :inflib,
52
+ :properties, :links, :modules, :tags, :created_at, :modified_at,
53
+ :visited
54
+
55
+ alias values properties
56
+
57
+ # rubocop: disable Metrics/AbcSize
58
+ # rubocop: disable Metrics/MethodLength
59
+ def initialize(textual_name = nil, *args, &block)
60
+ super(*args)
61
+ @name = textual_name || self.class.name
62
+ @short_name = textual_name || format(
63
+ DefaultShortNameTemplate, short_name: self.class.name)
64
+ @display_name = textual_name
65
+ @description = nil
66
+ @adjectives = []
67
+ @properties = {}
68
+ @links = []
69
+ @modules = []
70
+ @tags = []
71
+ @children = []
72
+ @visited = []
73
+ @created_at = @modified_at = Time.now
74
+ index_words if respond_to?(:index_words)
75
+ EphemeralObjects[self.identity] = self
76
+ EphemeralObjects[@display_name.to_s] = self unless @display_name.nil?
77
+ self.with(&block) unless block.nil?
78
+ end
79
+ # rubocop: enable Metrics/AbcSize
80
+ # rubocop: enable Metrics/MethodLength
81
+
82
+ def self.fetch_or_create_by_name(name)
83
+ key = name.to_s
84
+ object = EphemeralObjects[key]
85
+ return object if object.is_a?(self)
79
86
 
80
- def self.fetch_or_create_by_name(object_name, klass = self)
81
- Inform::EphemeralObjects.fetch(object_name) do
82
- Inform::EphemeralObjects[object_name] = klass.create(object_name)
87
+ new(key).tap { |created| EphemeralObjects[key] = created }
83
88
  end
84
- end
85
89
 
86
- def self.create(name = nil, object_type: nil, parent_id: nil, properties: {}, klass: self)
87
- klass.new(name).tap do |obj|
88
- obj.object_type = object_type
89
- obj.parent_id = parent_id
90
- obj.properties.merge!(properties)
90
+ def self.all
91
+ EphemeralObjects.values.select { |object| object.is_a?(self) }.uniq
91
92
  end
92
- end
93
93
 
94
- def values
95
- {
96
- name: @name,
97
- short_name: @short_name,
98
- description: @description,
99
- properties: @properties
100
- }
101
- end
94
+ def self.clear
95
+ EphemeralObjects.clear
96
+ EphemeralObjectsChildren.clear if defined?(EphemeralObjectsChildren)
97
+ end
102
98
 
103
- def index_obj(obj)
104
- if Instances.memo.respond_to?(:add)
105
- Instances.memo.add(obj)
106
- else
107
- Instances.memo << obj
99
+ def self.[](id)
100
+ object = EphemeralObjects[id]
101
+ return object if object.is_a?(self)
102
+
103
+ all.find { |candidate| candidate.id == id || candidate.identity == id }
108
104
  end
109
- end
110
105
 
111
- def self.all
112
- Instances.memo.dup
113
- end
106
+ def [](property)
107
+ return description if property == :description
108
+ return name if property == :name
109
+ return short_name if property == :short_name
114
110
 
115
- def self.clear
116
- Instances.memo.clear
117
- end
111
+ properties[property]
112
+ end
118
113
 
119
- def [](property)
120
- return description if property == :description
121
- return name if property == :name
122
- return short_name if property == :short_name
123
- properties[property]
124
- end
114
+ def to_hash
115
+ properties.merge(
116
+ id: id,
117
+ name: name,
118
+ short_name: short_name,
119
+ description: description
120
+ )
121
+ end
125
122
 
126
- def to_hash
127
- properties.merge(
128
- id: id,
129
- name: name,
130
- short_name: short_name,
131
- description: description
132
- )
133
- end
123
+ def <=>(other)
124
+ self.name <=> other.name
125
+ end
134
126
 
135
- def <=>(other)
136
- self.name <=> other.name
137
- end
127
+ def parse(s)
128
+ return if inflib.nil?
138
129
 
139
- def parse(s)
140
- return if inflib.nil?
141
- inflib.semaphore.synchronize do
142
- inflib.println(inflib.parse(s))
130
+ inflib.semaphore.synchronize { inflib.parse(s) }
143
131
  end
144
- end
145
132
 
146
- def id
147
- self.object_id
148
- end
133
+ def id
134
+ self.object_id
135
+ end
149
136
 
150
- def object_type
151
- self.class
152
- end
137
+ def object_type
138
+ self.class
139
+ end
153
140
 
154
- def name(*args)
155
- return args.first.object_name if args.first.respond_to?(:object_name)
156
- return @name if args.empty?
157
- @name = ([@name] | args).flatten.uniq
158
- index_words if respond_to?(:index_words)
159
- @name
160
- end
141
+ def name(*args)
142
+ return args.first.object_name if args.first.respond_to?(:object_name)
143
+ return @name if args.empty?
161
144
 
162
- def name=(*args)
163
- return if args.empty?
164
- @name = args.first.object_name if args.first.respond_to?(:object_name)
165
- @name = ([@name] | args).flatten.uniq
166
- index_words if respond_to?(:index_words)
167
- return
168
- end
145
+ @name = ([@name] | args).flatten.uniq
146
+ index_words if respond_to?(:index_words)
147
+ @name
148
+ end
169
149
 
170
- def description(*args)
171
- return @description if args.nil? || args.empty?
172
- @description = args.length > 1 ? args.join(' ') : args.first
173
- @description
174
- end
150
+ def description(description = nil)
151
+ return @description if description.nil?
175
152
 
176
- def object_name
177
- @display_name || @short_name || @name
178
- end
153
+ @description = description
154
+ end
179
155
 
180
- def name_words
181
- @name.split(/[,\s]+/).join(', ')
182
- end
156
+ alias description= description
183
157
 
184
- def <<(o)
185
- return if self == o
186
- return unless o.object?
187
- o.parent = self
188
- @children << o
189
- end
158
+ def object_name
159
+ @display_name || @short_name || @name
160
+ end
190
161
 
191
- def remove
192
- @children.clear
193
- @parent = nil
194
- end
162
+ def name_words
163
+ name_words = @name
164
+ name_words = name_words.split(/[,\s]+/) if name_words.respond_to?(:split)
165
+ name_words.join(', ') if name_words.respond_to?(:join)
166
+ name_words
167
+ end
195
168
 
196
- def empty?(*args, &block)
197
- self.children.empty?(*args, &block)
198
- end
169
+ def <<(o)
170
+ return if self == o
171
+ return unless o.object?
199
172
 
200
- def children(o = nil, prop = :@children)
201
- return @children if o.nil?
202
- return o.instance_variable_get(prop).length if o.instance_variable_defined?(prop)
203
- o.children.length
204
- end
173
+ o.parent = self
174
+ @children << o
175
+ end
205
176
 
206
- # TODO: Remove if possible after unit test implementation
207
- def location
208
- self.parent
209
- end
177
+ def remove
178
+ @children.clear
179
+ @parent = nil
180
+ end
210
181
 
211
- def list_together
212
- linkto :list_together
213
- end
182
+ def empty?(...)
183
+ self.children.empty?(...)
184
+ end
214
185
 
215
- def ancestors
216
- self.parent.nil? ? [] : self.parent.ancestors + [self.parent]
217
- end
186
+ def children(o = nil, prop = :@children)
187
+ return @children if o.nil?
188
+ return o.instance_variable_get(prop).length if o.instance_variable_defined?(prop)
218
189
 
219
- def descendants
220
- @children + @children.map(&:descendants).flatten
221
- end
190
+ o.children.length
191
+ end
222
192
 
223
- def root
224
- self.parent.nil? ? self : self.parent.root
225
- end
193
+ def location
194
+ self.parent
195
+ end
226
196
 
227
- def parent(o = nil, &block)
228
- return @parent if o.nil?
229
- o.safe_refresh if o.respond_to? :safe_refresh
230
- return unless o.respond_to? :parent
231
- obj_parent = block_given? ? parent_selector(o, &block) : o.parent
232
- return nil if obj_parent.nil?
233
- obj_parent.safe_refresh if obj_parent.respond_to? :safe_refresh
234
- obj_parent
235
- end
197
+ def list_together
198
+ linkto :list_together
199
+ end
236
200
 
237
- # rubocop: disable Style/TrivialAccessors
238
- def parent=(o)
239
- @parent = o
240
- end
241
- # rubocop: enable Style/TrivialAccessors
201
+ def ancestors
202
+ self.parent.nil? ? [] : self.parent.ancestors + [self.parent]
203
+ end
242
204
 
243
- def _set_object(link_name, obj = nil)
244
- link(link_name, obj).to
245
- end
205
+ def descendants
206
+ @children + @children.map(&:descendants).flatten
207
+ end
246
208
 
247
- def _get_object(link_name)
248
- find_link(link_name)&.to
249
- end
209
+ def root
210
+ self.parent.nil? ? self : self.parent.root
211
+ end
250
212
 
251
- def find_link(link_name)
252
- links.find { |x| x.name == link_name }
253
- end
213
+ def parent(o = nil, &block)
214
+ return @parent if o.nil?
254
215
 
255
- def link(link_name, obj = nil)
256
- link = Inform::Ephemeral::Link.new(name: link_name, to: obj, from: self)
257
- links << link
258
- link
259
- end
216
+ o.safe_refresh if o.respond_to? :safe_refresh
217
+ return unless o.respond_to? :parent
260
218
 
261
- def linked?(link_name)
262
- links.count { |x| x.name == link_name } > 0
263
- end
264
- alias _key? linked?
219
+ obj_parent = block_given? ? parent_selector(o, &block) : o.parent
220
+ return nil if obj_parent.nil?
265
221
 
266
- def unlink(link_name)
267
- links.delete_at(links.index { |x| x.name == link_name })
268
- end
269
- alias _unset_object unlink
222
+ obj_parent.safe_refresh if obj_parent.respond_to? :safe_refresh
223
+ obj_parent
224
+ end
270
225
 
271
- def linkto(link_name)
272
- _get_object(link_name)
273
- end
226
+ # rubocop: disable Style/TrivialAccessors
227
+ def parent=(o)
228
+ @parent = o
229
+ end
230
+ # rubocop: enable Style/TrivialAccessors
274
231
 
275
- def linksfrom(link_name = nil)
276
- if link_name.nil?
277
- links.select { |x| x.to == self }.map(&:from)
278
- else
279
- links.select { |x| x.name == link_name && x.to == self }.map(&:from)
232
+ def _set_object(link_name, obj = nil)
233
+ link(link_name, obj).to
280
234
  end
281
- end
282
235
 
283
- def init
284
- self
285
- end
286
- alias safe_init init
236
+ def _get_object(link_name)
237
+ find_link(link_name)&.to
238
+ end
287
239
 
288
- def refresh
289
- self
290
- end
291
- alias safe_refresh refresh
240
+ def find_link(link_name)
241
+ links.find { |x| x.name == link_name }
242
+ end
292
243
 
293
- def save(*_args)
294
- self
295
- end
296
- alias safe_save save
244
+ def link(link_name, obj = nil)
245
+ link = Inform::System::Link.new(name: link_name, to: obj, from: self)
246
+ links << link
247
+ link
248
+ end
297
249
 
298
- def to_s
299
- self.name
300
- end
250
+ def linked?(link_name)
251
+ links.count { |x| x.name == link_name } > 0
252
+ end
253
+
254
+ alias _key? linked?
301
255
 
302
- def inspect
303
- identity
256
+ def unlink(link_name)
257
+ links.delete_at(links.index { |x| x.name == link_name })
258
+ end
259
+
260
+ alias _unset_object unlink
261
+
262
+ def linkto(link_name)
263
+ _get_object(link_name)
264
+ end
265
+
266
+ def linksfrom(link_name = nil)
267
+ if link_name.nil?
268
+ links.select { |x| x.to == self }.map(&:from)
269
+ else
270
+ links.select { |x| x.name == link_name && x.to == self }.map(&:from)
271
+ end
272
+ end
273
+
274
+ def init
275
+ self
276
+ end
277
+
278
+ alias safe_init init
279
+
280
+ def refresh
281
+ self
282
+ end
283
+
284
+ alias safe_refresh refresh
285
+
286
+ def save(*_args)
287
+ self
288
+ end
289
+
290
+ alias safe_save save
304
291
  end
292
+ # rubocop: enable Metrics/ClassLength
293
+ # class Object
305
294
  end
306
- # rubocop: enable Metrics/ClassLength
307
- # class Inform::Ephemeral::Object
308
- end
309
- # module Inform::Ephemeral
295
+ # module Ephemeral
310
296
  end
311
297
  # module Inform
312
298
 
313
299
  # The Inform module
314
300
  module Inform
315
- unless defined?(Inform::EphemeralObjects)
316
- EphemeralObjects = if defined?(Java)
317
- java.util.concurrent.ConcurrentHashMap.new
318
- else
319
- {}
301
+ unless defined?(Inform::EphemeralObjects)
302
+ EphemeralObjects = if defined?(Java)
303
+ java.util.concurrent.ConcurrentHashMap.new
304
+ else
305
+ {}
306
+ end
320
307
  end
321
- end
322
- unless defined?(Inform::EphemeralObjectsChildren)
323
- EphemeralObjectsChildren = if defined?(Java)
324
- java.util.concurrent.ConcurrentHashMap.new
325
- else
326
- {}
308
+
309
+ unless defined?(Inform::EphemeralObjectsChildren)
310
+ EphemeralObjectsChildren = if defined?(Java)
311
+ java.util.concurrent.ConcurrentHashMap.new
312
+ else
313
+ {}
314
+ end
327
315
  end
328
316
  end
329
- end
@@ -1,7 +1,8 @@
1
+ # lib/story_teller/inform/tag.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
  #
@@ -25,7 +26,7 @@ module Inform
25
26
  # module Ephemeral
26
27
  module Ephemeral
27
28
  # class Tag
28
- class Tag < Inform::Tag
29
+ class Tag
29
30
  attr_accessor :created_at, :modified_at, :name
30
31
 
31
32
  def initialize(name)
@@ -94,7 +95,7 @@ end
94
95
  end
95
96
  # module Inform
96
97
 
97
- # The Inform module
98
+ # module Inform
98
99
  module Inform
99
100
  # The TagRegistry class
100
101
  class TagRegistry < Set
@@ -106,8 +107,8 @@ module Inform
106
107
  end
107
108
 
108
109
  def all_persisted_tags
109
- return [] unless Inform::Tag.respond_to?(:map)
110
- Inform::Tag.map { |a| a.name.to_sym }
110
+ return [] unless Inform.tag_klass.respond_to?(:map)
111
+ Inform.tag_klass.map { |a| a.name.to_sym }
111
112
  rescue Sequel::DatabaseError => e
112
113
  log.warn e.message
113
114
  return []
@@ -121,8 +122,18 @@ module Inform
121
122
  end
122
123
  alias reset init
123
124
  end
125
+ end
126
+ # module Inform
127
+
128
+ # module Inform
129
+ module Inform
130
+ module_function
124
131
 
125
- def self.attributes
132
+ def tag_klass
133
+ Inform::Ephemeral::Tag
134
+ end
135
+
136
+ def attributes
126
137
  TagRegistry::SINGLETON.memo ||= TagRegistry.new
127
138
  end
128
139
  end
@@ -134,10 +145,9 @@ module Inform
134
145
  # module Ephemeral
135
146
  module Ephemeral
136
147
  # The Inform::Tagged class
137
- class Tagged < Inform::Tagged; end
148
+ class Tagged; end
138
149
  end
139
150
  end
140
- # module Inform
141
151
 
142
152
  # Taggable
143
153
 
@@ -155,33 +165,36 @@ module Inform
155
165
  end
156
166
  end
157
167
 
158
- # The Inform module
168
+ # module Inform
159
169
  module Inform
160
- # The Inform::Taggable module
170
+ # module Taggable
161
171
  module Taggable
162
172
  include TagHelpers
163
173
 
164
174
  # rubocop: disable Metrics/AbcSize
175
+ # rubocop: disable Metrics/MethodLength
165
176
  def tag(*args)
166
177
  # TODO: Generalize workflag to system flags
167
178
  workflags.add(self.identity) if args.delete(:workflag)
168
179
  a = self.tag_names args
169
180
  (a - (self.nil_safe_tags & a)).each do |tag|
170
- if Inform::Tag.respond_to?(:find_or_create)
171
- add_tag(Inform::Tag.find_or_create(name: tag.to_s))
181
+ if Inform.tag_klass.respond_to?(:find_or_create)
182
+ add_tag(Inform.tag_klass.find_or_create(name: tag.to_s))
172
183
  else
173
- add_tag(tags.find { |tag| tag.name == tag.to_s } || Inform::Tag.new(tag.to_s))
184
+ tag = tags.find { |tag| tag.name == tag.to_s } || Inform.tag_klass.new(tag.to_s)
185
+ add_tag(tag)
174
186
  end
175
187
  end
176
188
  self.save_changes if self.respond_to?(:save_changes)
177
189
  end
178
190
  # rubocop: enable Metrics/AbcSize
191
+ # rubocop: enable Metrics/MethodLength
179
192
 
180
193
  def untag(*args)
181
194
  # TODO: Generalize workflag to system flags
182
195
  workflags.delete(self.identity) if args.delete(:workflag)
183
196
  (self.nil_safe_tags & tag_names(args)).each do |tag|
184
- remove_tag Inform::Tag.find(name: tag.to_s)
197
+ remove_tag(Inform.tag_klass.find(name: tag.to_s))
185
198
  end
186
199
  self.save_changes if self.respond_to?(:save_changes)
187
200
  end