inform-runtime 1.0.4

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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +623 -0
  3. data/README.md +185 -0
  4. data/Rakefile +65 -0
  5. data/config/database.yml +37 -0
  6. data/exe/inform.rb +6 -0
  7. data/game/config.yml +5 -0
  8. data/game/example.inf +76 -0
  9. data/game/example.rb +90 -0
  10. data/game/forms/example_form.rb +2 -0
  11. data/game/grammar/game_grammar.inf.rb +11 -0
  12. data/game/languages/english.rb +2 -0
  13. data/game/models/example_model.rb +2 -0
  14. data/game/modules/example_module.rb +9 -0
  15. data/game/rules/example_state.rb +2 -0
  16. data/game/scripts/example_script.rb +2 -0
  17. data/game/topics/example_topic.rb +2 -0
  18. data/game/verbs/game_verbs.rb +15 -0
  19. data/game/verbs/metaverbs.rb +2028 -0
  20. data/lib/runtime/articles.rb +138 -0
  21. data/lib/runtime/builtins.rb +359 -0
  22. data/lib/runtime/color.rb +145 -0
  23. data/lib/runtime/command.rb +470 -0
  24. data/lib/runtime/config.rb +48 -0
  25. data/lib/runtime/context.rb +78 -0
  26. data/lib/runtime/daemon.rb +266 -0
  27. data/lib/runtime/database.rb +500 -0
  28. data/lib/runtime/events.rb +771 -0
  29. data/lib/runtime/experimental/handler_dsl.rb +175 -0
  30. data/lib/runtime/game.rb +74 -0
  31. data/lib/runtime/game_loader.rb +132 -0
  32. data/lib/runtime/grammar_parser.rb +553 -0
  33. data/lib/runtime/helpers.rb +177 -0
  34. data/lib/runtime/history.rb +45 -0
  35. data/lib/runtime/inflector.rb +195 -0
  36. data/lib/runtime/io.rb +174 -0
  37. data/lib/runtime/kernel.rb +450 -0
  38. data/lib/runtime/library.rb +59 -0
  39. data/lib/runtime/library_loader.rb +135 -0
  40. data/lib/runtime/link.rb +158 -0
  41. data/lib/runtime/logging.rb +197 -0
  42. data/lib/runtime/mixins.rb +570 -0
  43. data/lib/runtime/module.rb +202 -0
  44. data/lib/runtime/object.rb +761 -0
  45. data/lib/runtime/options.rb +104 -0
  46. data/lib/runtime/persistence.rb +292 -0
  47. data/lib/runtime/plurals.rb +60 -0
  48. data/lib/runtime/prototype.rb +307 -0
  49. data/lib/runtime/publication.rb +92 -0
  50. data/lib/runtime/runtime.rb +321 -0
  51. data/lib/runtime/session.rb +202 -0
  52. data/lib/runtime/stdlib.rb +604 -0
  53. data/lib/runtime/subscription.rb +47 -0
  54. data/lib/runtime/tag.rb +287 -0
  55. data/lib/runtime/tree.rb +204 -0
  56. data/lib/runtime/version.rb +24 -0
  57. data/lib/runtime/world_tree.rb +69 -0
  58. data/lib/runtime.rb +35 -0
  59. metadata +199 -0
@@ -0,0 +1,158 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
+ #
6
+ # This file is part of the Inform Runtime.
7
+ #
8
+ # The Inform Runtime is free software: you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # The Inform Runtime is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ # Link
22
+
23
+ if defined?(Sequel::Migration)
24
+ # The LinkSetup class
25
+ class LinkSetup < Sequel::Migration
26
+ # rubocop: disable Metrics/MethodLength
27
+ def up
28
+ # return if table_exists? :link
29
+ log.debug "#up"
30
+ create_table? :link do
31
+ primary_key :id
32
+ foreign_key :from_id, :object, on_delete: :cascade
33
+ foreign_key :to_id, :object, on_delete: :cascade
34
+ index :name
35
+ index :created_at
36
+
37
+ String :name
38
+ DateTime :created_at
39
+ DateTime :modified_at
40
+ end
41
+ end
42
+ # rubocop: enable Metrics/MethodLength
43
+
44
+ def down
45
+ drop_table :link if table_exists? :link
46
+ end
47
+ end
48
+ # class LinkSetup
49
+ end
50
+ # defined?(Sequel::Migration)
51
+
52
+ # The Inform module
53
+ module Inform
54
+ # The Inform::Link class
55
+ class Link < Sequel::Model
56
+ set_primary_key :id
57
+ def_column_accessor :created_at, :modified_at
58
+ def_column_accessor :name
59
+ many_to_one :from, class: Inform::Object, key: :from_id
60
+ many_to_one :to, class: Inform::Object, key: :to_id
61
+
62
+ LinkTemplate = '%<link_name>s -> %<to_name>s [%<to_identity>s]'.freeze
63
+ LinkMethods = %w[from_id to_id].freeze
64
+ MethodWriterTemplate = '%<method_name>s='.freeze
65
+
66
+ def before_create
67
+ self.created_at ||= Time.now
68
+ super
69
+ end
70
+
71
+ def after_save
72
+ super
73
+ self.modified_at = Time.now.utc
74
+ end
75
+
76
+ def to_s
77
+ format(LinkTemplate, link_name: name, to_name: to.name, to_identity: to.identity)
78
+ end
79
+
80
+ def <=>(other)
81
+ self.name <=> other.name
82
+ end
83
+
84
+ def init_with(coder)
85
+ LinkMethods.each do |method_name|
86
+ method_symbol = format(MethodWriterTemplate, method_name: method_name).to_sym
87
+ self.send(method_symbol, coder[method_name]) if self.respond_to? method_symbol
88
+ end
89
+ self
90
+ end
91
+ end
92
+ end
93
+
94
+ # The Inform module
95
+ module Inform
96
+ # The Linkable module
97
+ module Linkable
98
+ def links
99
+ super
100
+ rescue StandardError => _e
101
+ Array::Empty
102
+ end
103
+
104
+ # rubocop: disable Metrics/AbcSize
105
+ # rubocop: disable Metrics/MethodLength
106
+ def link(link_name, obj = nil)
107
+ link = Inform::Link.first(name: link_name.to_s, from_id: self.id)
108
+ link&.to&.refresh
109
+ return link if obj.nil?
110
+ if link.nil?
111
+ clauses = { name: link_name.to_s, from_id: self.id, to_id: obj.id }
112
+ link = Inform::Link.find_or_create(**clauses) do |l|
113
+ l.from = self
114
+ l.to = obj
115
+ end
116
+ else
117
+ link.to = obj
118
+ link.save
119
+ end
120
+ link
121
+ end
122
+ # rubocop: enable Metrics/AbcSize
123
+ # rubocop: enable Metrics/MethodLength
124
+
125
+ def linked?(link_name)
126
+ Inform::Link.filter(name: link_name.to_s, from_id: self.id).count > 0
127
+ end
128
+
129
+ def unlink(link_name)
130
+ Inform::Link.first(name: link_name.to_s, from_id: self.id).destroy&.to
131
+ rescue Sequel::NoExistingObject => e
132
+ log.warn 'Error: ' + e.message
133
+ log.warn 'No such link: ' + link_name
134
+ nil
135
+ end
136
+
137
+ def linkto(link_name)
138
+ Inform::Link.first(name: link_name.to_s, from_id: self.id)&.to&.refresh
139
+ end
140
+
141
+ def linksfrom(link_name = nil)
142
+ if link_name.nil?
143
+ Inform::Link.filter(to_id: self.id).map(&:from)
144
+ else
145
+ Inform::Link.filter(name: link_name.to_s, to_id: self.id).map(&:from)
146
+ end
147
+ end
148
+
149
+ alias _key? linked?
150
+ alias _get_object linkto
151
+ def _set_object(link_name, obj = nil)
152
+ link(link_name, obj).to
153
+ end
154
+ alias _unset_object unlink
155
+ end
156
+ # module Linkable
157
+ end
158
+ # module Inform
@@ -0,0 +1,197 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: false
3
+
4
+ # Copyright Nels Nelson 2008-2023 but freely usable (see license)
5
+ #
6
+ # This file is part of the Inform Runtime.
7
+ #
8
+ # The Inform Runtime is free software: you can redistribute it and/or
9
+ # modify it under the terms of the GNU General Public License as published
10
+ # by the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # The Inform Runtime is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with the Inform Runtime. If not, see <http://www.gnu.org/licenses/>.
20
+
21
+ require 'fileutils'
22
+ require 'logger'
23
+
24
+ if defined?(Java)
25
+ require 'apache-log4j-2'
26
+ end
27
+
28
+ # The Logging module
29
+ module Logging
30
+ # rubocop: disable Metrics/MethodLength
31
+ def config
32
+ @config ||= begin
33
+ support_dir_path = File.expand_path(__dir__)
34
+ lib_dir_path = File.expand_path(File.dirname(support_dir_path))
35
+ project_dir_path = File.expand_path(File.dirname(lib_dir_path))
36
+ app_name = File.basename(project_dir_path)
37
+ logs_dir_path = File.expand_path(File.join(project_dir_path, 'logs'))
38
+ FileUtils.mkdir_p(logs_dir_path)
39
+ log_file = File.expand_path(File.join(logs_dir_path, "#{app_name}.log"))
40
+ {
41
+ level: :info,
42
+ name: app_name,
43
+ lib_dir_path: lib_dir_path,
44
+ project_dir_path: project_dir_path,
45
+ logs_dir_path: logs_dir_path,
46
+ log_file: log_file,
47
+ rolling_log_file_name_template: "#{app_name}-%d{yyyy-MM-dd}.log.gz",
48
+ logging_timestamp_format: '%Y-%m-%d %H:%M:%S',
49
+ logging_pattern_template: {
50
+ java: '%d{ABSOLUTE} %-5p [%c{1}] %m%n',
51
+ ruby: "%<timestamp>s %-5<severity>s [%<progname>s] %<msg>s\n"
52
+ },
53
+ schedule: '0 0 0 * * ?',
54
+ size: '100M'
55
+ }
56
+ end
57
+ end
58
+ module_function :config
59
+ # rubocop: enable Metrics/MethodLength
60
+ end
61
+
62
+ # Namespace for methods to help with implicit backtrace printing
63
+ module LoggerHelpers
64
+ def generate_message(error_or_message, error)
65
+ error_message = "#{error_or_message}: #{error.class.name}"
66
+ error_message << ": #{error.message}" if error.respond_to?(:message)
67
+ error_message
68
+ end
69
+
70
+ def extract_backtrace(error, default_result = nil)
71
+ if error.respond_to?(:backtrace)
72
+ error.backtrace.each { |trace| original_error(trace) unless trace.nil? }
73
+ elsif error.respond_to?(:getStackTrace)
74
+ error.getStackTrace().each { |trace| original_error(trace) unless trace.nil? }
75
+ else
76
+ default_result
77
+ end
78
+ end
79
+ end
80
+
81
+ # Monkey-patch the built-in Ruby Logger class to support
82
+ # implicit backtrace printing
83
+ # TODO: Figure out if this is actually useful.
84
+ class Logger
85
+ include LoggerHelpers
86
+
87
+ alias original_error error
88
+ def error(error_or_message, error = nil)
89
+ warn error_or_message
90
+ return extract_backtrace(error_or_message) if error.nil?
91
+ original_error(generate_message(error_or_message, error))
92
+ extract_backtrace(original_error(error))
93
+ end
94
+ end
95
+
96
+ # The Logging module
97
+ module Logging
98
+ if defined?(Java)
99
+ java_import Java::org.apache.logging.log4j.Level
100
+ java_import Java::org.apache.logging.log4j.LogManager
101
+ end
102
+
103
+ def init_logger(level = :info, logger_name = nil)
104
+ return init_java_logger(level, logger_name, caller[2]) if defined?(Java)
105
+ init_ruby_logger(level, logger_name, caller[2])
106
+ end
107
+
108
+ def ruby_log_formatter(severity, datetime, progname, message)
109
+ format(
110
+ Logging.config[:logging_pattern_template][:ruby],
111
+ timestamp: datetime.strftime(Logging.config[:logging_timestamp_format]),
112
+ progname: progname, severity: severity, msg: message)
113
+ end
114
+
115
+ ForwardSlashPattern = %r{/}.freeze
116
+
117
+ def init_ruby_logger(level = nil, logger_name = nil, source_location = nil)
118
+ logger_name = get_formatted_logger_name(logger_name)
119
+ logger_name = source_location.split(ForwardSlashPattern).last if logger_name.empty?
120
+ log = Logger.new($stdout, progname: logger_name)
121
+ log.level = level.to_s unless level.nil?
122
+ log.formatter = method(:ruby_log_formatter)
123
+ log
124
+ end
125
+
126
+ def init_java_logger(level = nil, logger_name = nil, source_location = nil)
127
+ logger_name = get_formatted_logger_name(logger_name)
128
+ logger_name = source_location.split(ForwardSlashPattern).last if logger_name.empty?
129
+ log = LogManager.getLogger(logger_name)
130
+ log.level = Level.to_level(level.to_s.upcase) unless level.nil?
131
+ log
132
+ end
133
+
134
+ def get_formatted_logger_name(logger_name = nil)
135
+ return logger_name.to_s[/\w+$/] unless logger_name.nil?
136
+ return name[/\w+$/] if is_a?(Class) || is_a?(Module)
137
+ self.class.name[/\w+$/]
138
+ end
139
+
140
+ # rubocop: disable Metrics/CyclomaticComplexity
141
+ # OFF: 0
142
+ # FATAL: 100
143
+ # ERROR: 200
144
+ # WARN: 300
145
+ # INFO: 400
146
+ # DEBUG: 500
147
+ # TRACE: 600
148
+ # ALL: 2147483647
149
+ # See: https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/Level.html
150
+ def symbolize_numeric_log_level(level)
151
+ case level
152
+ when 5..Float::INFINITY then :off
153
+ when 4 then :fatal
154
+ when 3 then :error
155
+ when 2 then :warn
156
+ when 1 then :info
157
+ when 0 then :debug
158
+ when -1 then :trace
159
+ when -Float::INFINITY..-2 then :all
160
+ end
161
+ end
162
+ # rubocop: enable Metrics/CyclomaticComplexity
163
+
164
+ def log_level=(level)
165
+ Logging.config[:level] = symbolize_numeric_log_level(level)
166
+ end
167
+ module_function :log_level=
168
+
169
+ def log_level
170
+ Logging.config[:level]
171
+ end
172
+ module_function :log_level
173
+
174
+ def log(level = Logging.log_level, log_name = Logging.config[:app_name])
175
+ @log ||= init_logger(level, log_name)
176
+ end
177
+ alias logger log
178
+ end
179
+ # module Logging
180
+
181
+ # The Module class
182
+ class Module
183
+ # Universally include Logging
184
+ include ::Logging
185
+ end
186
+
187
+ # The Class class
188
+ class Class
189
+ # Universally include Logging
190
+ include ::Logging
191
+ end
192
+
193
+ # The Object class
194
+ class Object
195
+ # Universally include Logging
196
+ include ::Logging
197
+ end