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.
- checksums.yaml +4 -4
- data/README.md +33 -41
- data/lib/story_teller/articles.rb +2 -1
- data/lib/story_teller/builtins.rb +63 -10
- data/lib/story_teller/color.rb +2 -1
- data/lib/story_teller/command.rb +4 -1
- data/lib/story_teller/context.rb +23 -27
- data/lib/story_teller/daemon.rb +2 -2
- data/lib/story_teller/engine.rb +80 -16
- data/lib/story_teller/events.rb +9 -2
- data/lib/story_teller/experimental/handler_dsl.rb +1 -16
- data/lib/story_teller/experimental/reverse_engineer_class.rb +1 -1
- data/lib/story_teller/grammar_parser.rb +60 -13
- data/lib/story_teller/helpers.rb +8 -18
- data/lib/story_teller/history.rb +2 -1
- data/lib/story_teller/inflector.rb +4 -2
- data/lib/story_teller/inform/ephemeral/link.rb +63 -31
- data/lib/story_teller/inform/ephemeral/module.rb +6 -5
- data/lib/story_teller/inform/ephemeral/object.rb +223 -236
- data/lib/story_teller/inform/ephemeral/tag.rb +27 -14
- data/lib/story_teller/io.rb +99 -49
- data/lib/story_teller/kernel.rb +13 -5
- data/lib/story_teller/library/bootstrap.rb +10 -27
- data/lib/story_teller/library/declarations.rb +1 -1
- data/lib/story_teller/library/directives.rb +1 -1
- data/lib/story_teller/library/loader.rb +4 -20
- data/lib/story_teller/library/location.rb +9 -3
- data/lib/story_teller/library.rb +1 -1
- data/lib/story_teller/logging.rb +165 -67
- data/lib/story_teller/mixins.rb +11 -4
- data/lib/story_teller/plurals.rb +2 -1
- data/lib/story_teller/privileges.rb +117 -0
- data/lib/story_teller/prototype.rb +158 -32
- data/lib/story_teller/publication.rb +2 -1
- data/lib/story_teller/session.rb +115 -25
- data/lib/story_teller/stdlib.rb +232 -2
- data/lib/story_teller/subscription.rb +2 -1
- data/lib/story_teller/tree.rb +2 -1
- data/lib/story_teller/version.rb +2 -2
- data/lib/story_teller/world_tree.rb +21 -23
- data/lib/story_teller.rb +18 -5
- metadata +7 -10
- data/lib/story_teller/core.rb +0 -38
- data/lib/story_teller/ephemeral_adapter.rb +0 -40
- data/lib/story_teller/inform/base.rb +0 -160
- data/lib/story_teller/model_adapter.rb +0 -132
data/lib/story_teller/logging.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# encoding: utf-8
|
|
3
3
|
# frozen_string_literal: false
|
|
4
4
|
|
|
5
|
-
# Copyright Nels Nelson 2008-
|
|
5
|
+
# Copyright Nels Nelson 2008-2026 but freely usable (see license)
|
|
6
6
|
#
|
|
7
7
|
# This file is part of StoryTeller.
|
|
8
8
|
#
|
|
@@ -26,26 +26,29 @@ if defined?(Java)
|
|
|
26
26
|
require 'apache-log4j-2'
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# The
|
|
30
|
-
module
|
|
29
|
+
# The LoggingConfiguration module
|
|
30
|
+
module LoggingConfiguration
|
|
31
|
+
# rubocop: disable Metrics/AbcSize
|
|
31
32
|
# rubocop: disable Metrics/MethodLength
|
|
32
33
|
def config
|
|
33
34
|
@config ||= begin
|
|
34
|
-
|
|
35
|
-
lib_dir_path = File.expand_path(File.dirname(
|
|
35
|
+
module_dir_path = File.expand_path(__dir__)
|
|
36
|
+
lib_dir_path = File.expand_path(File.dirname(module_dir_path))
|
|
36
37
|
project_dir_path = File.expand_path(File.dirname(lib_dir_path))
|
|
37
38
|
app_name = File.basename(project_dir_path)
|
|
38
39
|
logs_dir_path = File.expand_path(File.join(project_dir_path, 'logs'))
|
|
39
40
|
FileUtils.mkdir_p(logs_dir_path)
|
|
40
|
-
|
|
41
|
+
log_file_path = File.expand_path(File.join(logs_dir_path, "#{app_name}.log"))
|
|
42
|
+
FileUtils.touch(log_file_path)
|
|
41
43
|
{
|
|
42
|
-
level:
|
|
44
|
+
level: ENV['INFORM_LOG_LEVEL']&.to_i || Logger::INFO,
|
|
43
45
|
name: app_name,
|
|
44
46
|
lib_dir_path: lib_dir_path,
|
|
45
47
|
project_dir_path: project_dir_path,
|
|
46
48
|
logs_dir_path: logs_dir_path,
|
|
47
|
-
|
|
48
|
-
rolling_log_file_name_template:
|
|
49
|
+
log_file_path: log_file_path,
|
|
50
|
+
rolling_log_file_name_template: File.expand_path(
|
|
51
|
+
File.join(logs_dir_path, "#{app_name}-%d{yyyy-MM-dd}.log.gz")),
|
|
49
52
|
logging_timestamp_format: '%Y-%m-%d %H:%M:%S',
|
|
50
53
|
logging_pattern_template: {
|
|
51
54
|
java: '%d{ABSOLUTE} %-5p [%c{1}] %m%n',
|
|
@@ -57,103 +60,182 @@ module Logging
|
|
|
57
60
|
end
|
|
58
61
|
end
|
|
59
62
|
module_function :config
|
|
63
|
+
# rubocop: enable Metrics/AbcSize
|
|
64
|
+
# rubocop: enable Metrics/MethodLength
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The LoggingHelpers module
|
|
68
|
+
module LoggingHelpers
|
|
69
|
+
if defined?(Java)
|
|
70
|
+
java_import Java::org.apache.logging.log4j.core.appender::ConsoleAppender
|
|
71
|
+
java_import Java::org.apache.logging.log4j.core.config::Configurator
|
|
72
|
+
java_import Java::org.apache.logging.log4j.core.config.builder.api::ConfigurationBuilderFactory
|
|
73
|
+
java_import Java::org.apache.logging.log4j::Level
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# rubocop: disable Metrics/AbcSize
|
|
77
|
+
# rubocop: disable Metrics/MethodLength
|
|
78
|
+
def init_log4j(log_level = org.apache.logging.log4j.Level::INFO)
|
|
79
|
+
java.lang::System.setProperty('log4j.shutdownHookEnabled', java.lang::Boolean.toString(false))
|
|
80
|
+
config = ConfigurationBuilderFactory.newConfigurationBuilder()
|
|
81
|
+
|
|
82
|
+
log_level = Level.to_level(log_level.to_s.upcase) if log_level.is_a? Symbol
|
|
83
|
+
config.setStatusLevel(log_level)
|
|
84
|
+
config.setConfigurationName(LoggingConfiguration.config[:name])
|
|
85
|
+
|
|
86
|
+
# create a console appender
|
|
87
|
+
target = ConsoleAppender::Target::SYSTEM_OUT
|
|
88
|
+
pattern = LoggingConfiguration.config[:logging_pattern_template][:java]
|
|
89
|
+
layout = config.newLayout('PatternLayout')
|
|
90
|
+
layout = layout.addAttribute('pattern', pattern)
|
|
91
|
+
appender = config.newAppender('stdout', 'CONSOLE')
|
|
92
|
+
appender = appender.addAttribute('target', target)
|
|
93
|
+
appender = appender.add(layout)
|
|
94
|
+
config.add(appender)
|
|
95
|
+
|
|
96
|
+
# create a root logger
|
|
97
|
+
root_logger = config.newRootLogger(log_level)
|
|
98
|
+
root_logger = root_logger.add(config.newAppenderRef('stdout'))
|
|
99
|
+
|
|
100
|
+
# create a rolling file appender
|
|
101
|
+
cron = config.newComponent('CronTriggeringPolicy')
|
|
102
|
+
cron = cron.addAttribute('schedule', '0 0 0 * * ?')
|
|
103
|
+
|
|
104
|
+
size = config.newComponent('SizeBasedTriggeringPolicy')
|
|
105
|
+
size = size.addAttribute('size', LoggingConfiguration.config[:size])
|
|
106
|
+
|
|
107
|
+
policies = config.newComponent('Policies')
|
|
108
|
+
policies = policies.addComponent(cron)
|
|
109
|
+
policies = policies.addComponent(size)
|
|
110
|
+
|
|
111
|
+
appender = config.newAppender('rolling_file', 'RollingFile')
|
|
112
|
+
appender = appender.addAttribute(
|
|
113
|
+
'fileName', LoggingConfiguration.config[:log_file_path])
|
|
114
|
+
appender = appender.addAttribute(
|
|
115
|
+
'filePattern', LoggingConfiguration.config[:rolling_log_file_name_template])
|
|
116
|
+
appender = appender.add(layout)
|
|
117
|
+
appender = appender.addComponent(policies)
|
|
118
|
+
config.add(appender)
|
|
119
|
+
|
|
120
|
+
root_logger = root_logger.addAttribute('additivity', false)
|
|
121
|
+
root_logger = root_logger.add(config.newAppenderRef('rolling_file'))
|
|
122
|
+
config.add(root_logger)
|
|
123
|
+
|
|
124
|
+
logging_configuration = config.build()
|
|
125
|
+
ctx = Configurator.initialize(logging_configuration)
|
|
126
|
+
ctx.updateLoggers()
|
|
127
|
+
end
|
|
128
|
+
module_function :init_log4j
|
|
129
|
+
# rubocop: enable Metrics/AbcSize
|
|
60
130
|
# rubocop: enable Metrics/MethodLength
|
|
131
|
+
# def init_log4j
|
|
132
|
+
|
|
133
|
+
LoggingHelpers.init_log4j if defined?(Java)
|
|
61
134
|
end
|
|
135
|
+
# module Logging
|
|
62
136
|
|
|
63
137
|
# Namespace for methods to help with implicit backtrace printing
|
|
64
|
-
module
|
|
138
|
+
module TraceHelpers
|
|
65
139
|
def generate_message(error_or_message, error)
|
|
66
140
|
error_message = "#{error_or_message}: #{error.class.name}"
|
|
67
141
|
error_message << ": #{error.message}" if error.respond_to?(:message)
|
|
68
142
|
error_message
|
|
69
143
|
end
|
|
144
|
+
module_function :generate_message
|
|
70
145
|
|
|
71
146
|
def extract_backtrace(error, default_result = nil)
|
|
72
147
|
if error.respond_to?(:backtrace)
|
|
73
|
-
error.backtrace
|
|
148
|
+
error.backtrace || default_result
|
|
74
149
|
elsif error.respond_to?(:getStackTrace)
|
|
75
|
-
error.getStackTrace()
|
|
150
|
+
error.getStackTrace() || default_result
|
|
76
151
|
else
|
|
77
152
|
default_result
|
|
78
153
|
end
|
|
79
154
|
end
|
|
155
|
+
module_function :extract_backtrace
|
|
80
156
|
end
|
|
81
157
|
|
|
82
|
-
#
|
|
83
|
-
# implicit backtrace printing
|
|
84
|
-
# TODO: Figure out if this is actually useful.
|
|
158
|
+
# class Logger
|
|
85
159
|
class Logger
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
160
|
+
# module Severity
|
|
161
|
+
module Severity
|
|
162
|
+
# Lowest-level information, for developers.
|
|
163
|
+
TRACE = -1
|
|
164
|
+
LEVELS['trace'] = Logger::Severity::TRACE
|
|
165
|
+
end
|
|
166
|
+
end
|
|
89
167
|
|
|
168
|
+
# Add extra severity to the standard Logger class.
|
|
169
|
+
# Also add support for including backtraces implicitly.
|
|
170
|
+
class ExtraLogger < Logger
|
|
90
171
|
alias original_error error
|
|
172
|
+
def error(error_or_message = nil, error = nil, &block)
|
|
173
|
+
return log_error(error_or_message, &block) if error.nil?
|
|
91
174
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
extract_backtrace(original_error(error))
|
|
175
|
+
original_error(TraceHelpers.generate_message(error_or_message, error))
|
|
176
|
+
TraceHelpers.extract_backtrace(error, []).each do |trace|
|
|
177
|
+
original_error(trace) unless trace.nil?
|
|
178
|
+
end
|
|
97
179
|
end
|
|
98
180
|
|
|
99
|
-
def
|
|
100
|
-
|
|
181
|
+
def log_error(error_or_message = nil, &block)
|
|
182
|
+
original_error(error_or_message, &block)
|
|
183
|
+
TraceHelpers.extract_backtrace(error_or_message, []).each do |trace|
|
|
184
|
+
original_error(trace) unless trace.nil?
|
|
185
|
+
end
|
|
101
186
|
end
|
|
102
187
|
|
|
103
|
-
|
|
104
|
-
|
|
188
|
+
SEV_LABEL_EXTRA = (Logger::SEV_LABEL + %w(TRACE)).freeze
|
|
189
|
+
def format_severity(severity)
|
|
190
|
+
SEV_LABEL_EXTRA[severity] || super
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def trace(progname = nil, &block)
|
|
194
|
+
add(Logger::Severity::TRACE, nil, progname, &block)
|
|
105
195
|
end
|
|
106
196
|
end
|
|
107
197
|
|
|
108
|
-
# The
|
|
109
|
-
module
|
|
198
|
+
# The LoggingHelpers module
|
|
199
|
+
module LoggingHelpers
|
|
110
200
|
if defined?(Java)
|
|
111
201
|
java_import Java::org.apache.logging.log4j.Level
|
|
112
202
|
java_import Java::org.apache.logging.log4j.LogManager
|
|
113
203
|
end
|
|
114
204
|
|
|
115
|
-
|
|
116
|
-
all: -Float::INFINITY,
|
|
117
|
-
trace: Logger::TRACE,
|
|
118
|
-
debug: Logger::DEBUG,
|
|
119
|
-
info: Logger::INFO,
|
|
120
|
-
warn: Logger::WARN,
|
|
121
|
-
error: Logger::ERROR,
|
|
122
|
-
fatal: Logger::FATAL,
|
|
123
|
-
unknown: Logger::UNKNOWN,
|
|
124
|
-
off: Logger::UNKNOWN + 1
|
|
125
|
-
}.freeze
|
|
205
|
+
FORWARD_SLASH_PATTERN = %r{/} unless defined?(FORWARD_SLASH_PATTERN)
|
|
126
206
|
|
|
127
|
-
def init_logger(level = :info, logger_name = nil)
|
|
128
|
-
return init_java_logger(level, logger_name, caller[2]) if defined?(Java)
|
|
129
|
-
init_ruby_logger(level, logger_name, caller[2])
|
|
207
|
+
def init_logger(obj, level = :info, logger_name = nil)
|
|
208
|
+
return init_java_logger(obj, level, logger_name, caller[2]) if defined?(Java)
|
|
209
|
+
init_ruby_logger(obj, level, logger_name, caller[2])
|
|
130
210
|
end
|
|
211
|
+
module_function :init_logger
|
|
131
212
|
|
|
132
|
-
def ruby_log_formatter(
|
|
213
|
+
def ruby_log_formatter(severity_level, datetime, program_name, message)
|
|
133
214
|
format(
|
|
134
|
-
|
|
135
|
-
timestamp: datetime.strftime(
|
|
136
|
-
progname:
|
|
215
|
+
LoggingConfiguration.config[:logging_pattern_template][:ruby],
|
|
216
|
+
timestamp: datetime.strftime(LoggingConfiguration.config[:logging_timestamp_format]),
|
|
217
|
+
progname: program_name, severity: severity_level, msg: message)
|
|
137
218
|
end
|
|
219
|
+
module_function :ruby_log_formatter
|
|
138
220
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
logger_name = source_location.split(ForwardSlashPattern).last if logger_name.empty?
|
|
144
|
-
log = Logger.new($stdout, progname: logger_name)
|
|
221
|
+
def init_ruby_logger(obj, level = nil, logger_name = nil, source_location = nil)
|
|
222
|
+
logger_name = get_formatted_logger_name(obj, logger_name)
|
|
223
|
+
logger_name = source_location.split(FORWARD_SLASH_PATTERN).last if logger_name.empty?
|
|
224
|
+
log = ExtraLogger.new($stdout, progname: logger_name)
|
|
145
225
|
log.level = normalize_ruby_log_level(level) unless level.nil?
|
|
146
226
|
log.formatter = method(:ruby_log_formatter)
|
|
147
227
|
log
|
|
148
228
|
end
|
|
229
|
+
module_function :init_ruby_logger
|
|
149
230
|
|
|
150
|
-
def init_java_logger(level = nil, logger_name = nil, source_location = nil)
|
|
151
|
-
logger_name = get_formatted_logger_name(logger_name)
|
|
152
|
-
logger_name = source_location.split(
|
|
231
|
+
def init_java_logger(obj, level = nil, logger_name = nil, source_location = nil)
|
|
232
|
+
logger_name = get_formatted_logger_name(obj, logger_name)
|
|
233
|
+
logger_name = source_location.split(FORWARD_SLASH_PATTERN).last if logger_name.empty?
|
|
153
234
|
log = LogManager.getLogger(logger_name)
|
|
154
235
|
log.level = Level.to_level(level.to_s.upcase) unless level.nil?
|
|
155
236
|
log
|
|
156
237
|
end
|
|
238
|
+
module_function :init_java_logger
|
|
157
239
|
|
|
158
240
|
def normalize_ruby_log_level(level)
|
|
159
241
|
return level if level.is_a?(Integer)
|
|
@@ -162,12 +244,26 @@ module Logging
|
|
|
162
244
|
raise ArgumentError, "Unknown Ruby log level: #{level.inspect}"
|
|
163
245
|
end
|
|
164
246
|
end
|
|
247
|
+
module_function :normalize_ruby_log_level
|
|
165
248
|
|
|
166
|
-
def get_formatted_logger_name(logger_name = nil)
|
|
167
|
-
return logger_name.to_s
|
|
168
|
-
return name
|
|
169
|
-
|
|
249
|
+
def get_formatted_logger_name(obj, logger_name = nil)
|
|
250
|
+
return logger_name.to_s if !logger_name.nil? && logger_name.respond_to?(:to_s)
|
|
251
|
+
return obj.name if obj.is_a?(Class) || obj.is_a?(Module)
|
|
252
|
+
obj.class.name
|
|
170
253
|
end
|
|
254
|
+
module_function :get_formatted_logger_name
|
|
255
|
+
|
|
256
|
+
RubyLogLevels = {
|
|
257
|
+
all: -Float::INFINITY,
|
|
258
|
+
trace: Logger::TRACE,
|
|
259
|
+
debug: Logger::DEBUG,
|
|
260
|
+
info: Logger::INFO,
|
|
261
|
+
warn: Logger::WARN,
|
|
262
|
+
error: Logger::ERROR,
|
|
263
|
+
fatal: Logger::FATAL,
|
|
264
|
+
unknown: Logger::UNKNOWN,
|
|
265
|
+
off: Logger::UNKNOWN + 1
|
|
266
|
+
}.freeze
|
|
171
267
|
|
|
172
268
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
173
269
|
# OFF: 0
|
|
@@ -191,23 +287,25 @@ module Logging
|
|
|
191
287
|
when -Float::INFINITY..-2 then :all
|
|
192
288
|
end
|
|
193
289
|
end
|
|
290
|
+
module_function :symbolize_numeric_log_level
|
|
194
291
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
292
|
+
end
|
|
293
|
+
# module LoggingHelpers
|
|
195
294
|
|
|
295
|
+
# The Logging module
|
|
296
|
+
module Logging
|
|
196
297
|
def log_level=(level)
|
|
197
|
-
|
|
198
|
-
normalized_level = symbolize_numeric_log_level(level)
|
|
199
|
-
|
|
200
|
-
Logging.config[:level] = normalized_level
|
|
298
|
+
LoggingConfiguration.config[:level] = LoggingHelpers.symbolize_numeric_log_level(level)
|
|
201
299
|
end
|
|
202
300
|
module_function :log_level=
|
|
203
301
|
|
|
204
302
|
def log_level
|
|
205
|
-
|
|
303
|
+
LoggingConfiguration.config[:level]
|
|
206
304
|
end
|
|
207
305
|
module_function :log_level
|
|
208
306
|
|
|
209
|
-
def log(level = Logging.log_level, log_name =
|
|
210
|
-
@log ||= init_logger(level, log_name)
|
|
307
|
+
def log(level = Logging.log_level, log_name = LoggingConfiguration.config[:app_name])
|
|
308
|
+
@log ||= LoggingHelpers.init_logger(self, level, log_name)
|
|
211
309
|
end
|
|
212
310
|
alias logger log
|
|
213
311
|
end
|
data/lib/story_teller/mixins.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# lib/story_teller/mixins.rb
|
|
1
2
|
# encoding: utf-8
|
|
2
3
|
# frozen_string_literal: false
|
|
3
4
|
|
|
4
|
-
# Copyright Nels Nelson 2008-
|
|
5
|
+
# Copyright Nels Nelson 2008-2026 but freely usable (see license)
|
|
5
6
|
#
|
|
6
7
|
# This file is part of StoryTeller.
|
|
7
8
|
#
|
|
@@ -456,9 +457,15 @@ class Array
|
|
|
456
457
|
end
|
|
457
458
|
end
|
|
458
459
|
|
|
459
|
-
def sum(
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
def sum(init = nil)
|
|
461
|
+
if block_given?
|
|
462
|
+
initial = init.nil? ? 0 : init
|
|
463
|
+
inject(initial) { |total, element| total + yield(element) }
|
|
464
|
+
elsif init.nil?
|
|
465
|
+
inject(:+)
|
|
466
|
+
else
|
|
467
|
+
inject(init, :+)
|
|
468
|
+
end
|
|
462
469
|
end
|
|
463
470
|
|
|
464
471
|
def average
|
data/lib/story_teller/plurals.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
# lib/story_teller/plurals.rb
|
|
1
2
|
# encoding: utf-8
|
|
2
3
|
# frozen_string_literal: false
|
|
3
4
|
|
|
4
|
-
# Copyright Nels Nelson 2008-
|
|
5
|
+
# Copyright Nels Nelson 2008-2026 but freely usable (see license)
|
|
5
6
|
#
|
|
6
7
|
# This file is part of the StoryTeller.
|
|
7
8
|
#
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# lib/story_teller/privileges.rb
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
# frozen_string_literal: false
|
|
4
|
+
|
|
5
|
+
# Copyright Nels Nelson 2008-2026 but freely usable (see license)
|
|
6
|
+
#
|
|
7
|
+
# This file is part of the StoryTeller.
|
|
8
|
+
#
|
|
9
|
+
# The StoryTeller is free software: you can redistribute it and/or
|
|
10
|
+
# modify it under the terms of the GNU General Public License as published
|
|
11
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
|
12
|
+
# (at your option) any later version.
|
|
13
|
+
#
|
|
14
|
+
# The StoryTeller is distributed in the hope that it will be useful,
|
|
15
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
# GNU General Public License for more details.
|
|
18
|
+
#
|
|
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/>.
|
|
21
|
+
|
|
22
|
+
# module StoryTeller
|
|
23
|
+
module StoryTeller
|
|
24
|
+
# module PrivilegedIdentity
|
|
25
|
+
module PrivilegedIdentity
|
|
26
|
+
def privilege?(_privilege)
|
|
27
|
+
false
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def privilege_label
|
|
31
|
+
to_s
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The StoryTeller::Privileges module to implement privilege
|
|
36
|
+
module Privileges
|
|
37
|
+
class << self
|
|
38
|
+
def privileged?(subject, privilege)
|
|
39
|
+
StoryTeller::PrivilegeGrants.granted?(subject, privilege)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def subject_for(subject)
|
|
43
|
+
session_for(subject) || subject
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def session_for(subject)
|
|
49
|
+
return nil if subject.nil?
|
|
50
|
+
return nil unless defined?(StoryTeller::IO::Session)
|
|
51
|
+
return subject if subject.is_a?(StoryTeller::IO::Session)
|
|
52
|
+
return subject.instance_variable_get(:@session) if subject.instance_variable_defined?(:@session)
|
|
53
|
+
|
|
54
|
+
StoryTeller::IO::Session.of(subject)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def privileged?(privilege, subject = self)
|
|
59
|
+
StoryTeller::Privileges.privileged?(subject, privilege)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# module PrivilegeGrants
|
|
64
|
+
module PrivilegeGrants
|
|
65
|
+
@session_grants = Hash.new { |hash, key| hash[key] = Set.new }
|
|
66
|
+
@mode = :persistent
|
|
67
|
+
|
|
68
|
+
class << self
|
|
69
|
+
attr_accessor :mode
|
|
70
|
+
|
|
71
|
+
def grant_session(subject, privilege)
|
|
72
|
+
subject = StoryTeller::Privileges.subject_for(subject)
|
|
73
|
+
return nil if subject.nil?
|
|
74
|
+
|
|
75
|
+
@session_grants[identity(subject)].add(privilege.to_sym)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def session_granted?(subject, privilege)
|
|
79
|
+
subject = StoryTeller::Privileges.subject_for(subject)
|
|
80
|
+
return false if subject.nil?
|
|
81
|
+
|
|
82
|
+
@session_grants[identity(subject)].include?(privilege.to_sym)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def persistent_granted?(subject, privilege)
|
|
86
|
+
subject = StoryTeller::Privileges.subject_for(subject)
|
|
87
|
+
return false unless subject.is_a?(StoryTeller::PrivilegedIdentity)
|
|
88
|
+
|
|
89
|
+
subject.privilege?(privilege.to_sym)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def granted?(subject, privilege)
|
|
93
|
+
privilege = privilege.to_sym
|
|
94
|
+
|
|
95
|
+
session_granted?(subject, privilege) ||
|
|
96
|
+
(privilege == :builder && session_granted?(subject, :admin)) ||
|
|
97
|
+
persistent_granted_under_mode?(subject, privilege)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def persistent_granted_under_mode?(subject, privilege)
|
|
103
|
+
return false if mode == :session_only
|
|
104
|
+
|
|
105
|
+
persistent_granted?(subject, privilege) ||
|
|
106
|
+
(privilege == :builder && persistent_granted?(subject, :admin))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def identity(subject)
|
|
110
|
+
return subject.identity if subject.respond_to?(:identity)
|
|
111
|
+
|
|
112
|
+
subject.object_id
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
# module StoryTeller
|