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.
- checksums.yaml +7 -0
- data/LICENSE +623 -0
- data/README.md +185 -0
- data/Rakefile +65 -0
- data/config/database.yml +37 -0
- data/exe/inform.rb +6 -0
- data/game/config.yml +5 -0
- data/game/example.inf +76 -0
- data/game/example.rb +90 -0
- data/game/forms/example_form.rb +2 -0
- data/game/grammar/game_grammar.inf.rb +11 -0
- data/game/languages/english.rb +2 -0
- data/game/models/example_model.rb +2 -0
- data/game/modules/example_module.rb +9 -0
- data/game/rules/example_state.rb +2 -0
- data/game/scripts/example_script.rb +2 -0
- data/game/topics/example_topic.rb +2 -0
- data/game/verbs/game_verbs.rb +15 -0
- data/game/verbs/metaverbs.rb +2028 -0
- data/lib/runtime/articles.rb +138 -0
- data/lib/runtime/builtins.rb +359 -0
- data/lib/runtime/color.rb +145 -0
- data/lib/runtime/command.rb +470 -0
- data/lib/runtime/config.rb +48 -0
- data/lib/runtime/context.rb +78 -0
- data/lib/runtime/daemon.rb +266 -0
- data/lib/runtime/database.rb +500 -0
- data/lib/runtime/events.rb +771 -0
- data/lib/runtime/experimental/handler_dsl.rb +175 -0
- data/lib/runtime/game.rb +74 -0
- data/lib/runtime/game_loader.rb +132 -0
- data/lib/runtime/grammar_parser.rb +553 -0
- data/lib/runtime/helpers.rb +177 -0
- data/lib/runtime/history.rb +45 -0
- data/lib/runtime/inflector.rb +195 -0
- data/lib/runtime/io.rb +174 -0
- data/lib/runtime/kernel.rb +450 -0
- data/lib/runtime/library.rb +59 -0
- data/lib/runtime/library_loader.rb +135 -0
- data/lib/runtime/link.rb +158 -0
- data/lib/runtime/logging.rb +197 -0
- data/lib/runtime/mixins.rb +570 -0
- data/lib/runtime/module.rb +202 -0
- data/lib/runtime/object.rb +761 -0
- data/lib/runtime/options.rb +104 -0
- data/lib/runtime/persistence.rb +292 -0
- data/lib/runtime/plurals.rb +60 -0
- data/lib/runtime/prototype.rb +307 -0
- data/lib/runtime/publication.rb +92 -0
- data/lib/runtime/runtime.rb +321 -0
- data/lib/runtime/session.rb +202 -0
- data/lib/runtime/stdlib.rb +604 -0
- data/lib/runtime/subscription.rb +47 -0
- data/lib/runtime/tag.rb +287 -0
- data/lib/runtime/tree.rb +204 -0
- data/lib/runtime/version.rb +24 -0
- data/lib/runtime/world_tree.rb +69 -0
- data/lib/runtime.rb +35 -0
- metadata +199 -0
|
@@ -0,0 +1,500 @@
|
|
|
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 'sequel'
|
|
22
|
+
|
|
23
|
+
# The Sequel module
|
|
24
|
+
module Sequel
|
|
25
|
+
# The Sequel::Migration class
|
|
26
|
+
class Migration
|
|
27
|
+
def self.up
|
|
28
|
+
self.apply(Sequel::Model.db, :up)
|
|
29
|
+
end
|
|
30
|
+
def self.down
|
|
31
|
+
self.apply(Sequel::Model.db, :down)
|
|
32
|
+
end
|
|
33
|
+
def self.table_exists?(table)
|
|
34
|
+
Sequel::Model.db.table_exists?(table)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The Databases module
|
|
40
|
+
module Databases
|
|
41
|
+
# The Databases::Helpers module
|
|
42
|
+
module Helpers
|
|
43
|
+
MigrationMethods = %i[down up].freeze
|
|
44
|
+
NamespaceDelimiterPattern = /::/.freeze
|
|
45
|
+
DatabasePattern = /Database/.freeze
|
|
46
|
+
|
|
47
|
+
def class_derived_database_name
|
|
48
|
+
self.class.name.split(NamespaceDelimiterPattern).last.sub(DatabasePattern, '')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def implicit_database_name
|
|
52
|
+
implicit_database_name = self.class.name
|
|
53
|
+
begin
|
|
54
|
+
implicit_database_name = underscore(demodulize(class_derived_database_name)).to_sym
|
|
55
|
+
rescue StandardError => e
|
|
56
|
+
log.warn "Failed to get implicit database name: #{e.message}"
|
|
57
|
+
end
|
|
58
|
+
implicit_database_name
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def database
|
|
62
|
+
# implicit_database_name
|
|
63
|
+
underscore(Inform::Databases.instances.keys.first).to_sym
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def execute(sql)
|
|
67
|
+
Sequel::Model.db[sql]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def run(sql)
|
|
71
|
+
Sequel::Model.db.run(sql)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def database_exist?(database_name = database)
|
|
75
|
+
result = execute("select count(*) > 0 from pg_catalog.pg_database where datname = '#{database_name}';")&.first
|
|
76
|
+
result&.values&.first == true
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def user_exist?(username = database)
|
|
80
|
+
result = execute("select count(*) > 0 from pg_shadow where usename = '#{username}';")&.first
|
|
81
|
+
result&.values&.first == true
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def connected_database
|
|
85
|
+
return nil unless Sequel::Model.db
|
|
86
|
+
Sequel::Model.db.opts[:uri].to_s.scan(/:\/\/\w+(:\d+|\/)(\w+)/).last
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def connected_username
|
|
90
|
+
return nil unless Sequel::Model.db
|
|
91
|
+
Sequel::Model.db.opts[:uri].to_s.scan(/user=(\w+)/).first
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
if defined? GlobalSequelIdentityMap
|
|
97
|
+
# The Sequel module
|
|
98
|
+
module Sequel
|
|
99
|
+
# The Sequel::Plugins module
|
|
100
|
+
module Plugins
|
|
101
|
+
# The Sequel::Plugins::IdentityMap module
|
|
102
|
+
module IdentityMap
|
|
103
|
+
# The Sequel::Plugins::IdentityMap::ClassMethods module
|
|
104
|
+
module ClassMethods
|
|
105
|
+
# Returns the global thread-safe identity map.
|
|
106
|
+
def identity_map
|
|
107
|
+
log.debug "Getting identity map"
|
|
108
|
+
GlobalSequelIdentityMap
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
private
|
|
112
|
+
|
|
113
|
+
# Set the thread local identity map to the given value.
|
|
114
|
+
def identity_map=(v)
|
|
115
|
+
silence_warnings do
|
|
116
|
+
log.warn "Setting identity map"
|
|
117
|
+
Object.instance_eval { const_set :GlobalSequelIdentityMap, v }
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# The DatabaseConnectionHelpers module
|
|
127
|
+
module DatabaseConnectionHelpers
|
|
128
|
+
ConnectionAttempts = Struct.new(:memo).new(0)
|
|
129
|
+
DefaultConnectionOptions = { loggerLevel: 'OFF' }.freeze
|
|
130
|
+
DefaultConnectionPoolSize = 4
|
|
131
|
+
DefaultConnectionValidationTimeoutSeconds = 240
|
|
132
|
+
URLTemplate = '%<scheme>s://%<host>s%<colon>s%<port>s'.freeze
|
|
133
|
+
KeyValueTemplate = '%<key>s=%<value>s'.freeze
|
|
134
|
+
EmptyString = ''.freeze
|
|
135
|
+
AmpersandString = '&'.freeze
|
|
136
|
+
ColonString = ':'.freeze
|
|
137
|
+
ForwardSlashString = '/'.freeze
|
|
138
|
+
QuestionMarkString = '?'.freeze
|
|
139
|
+
JavaPattern = /java/i.freeze
|
|
140
|
+
|
|
141
|
+
# Previously:
|
|
142
|
+
# url = "#{adapter}://#{host}/#{database}?user=#{username}"
|
|
143
|
+
# url << "&password=#{password}" unless password.nil? || password.empty?
|
|
144
|
+
# url << "&loggerLevel=OFF"
|
|
145
|
+
# rubocop: disable Metrics/AbcSize
|
|
146
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
147
|
+
# rubocop: disable Metrics/MethodLength
|
|
148
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
149
|
+
def assemble_url
|
|
150
|
+
adapter = config.fetch('adapter')
|
|
151
|
+
adapter.gsub!(/^postgres/, 'jdbc:postgresql') if JavaPattern.match?(RUBY_PLATFORM)
|
|
152
|
+
host = config.fetch('host', nil)
|
|
153
|
+
port = config.fetch('port', nil)
|
|
154
|
+
colon = port.nil? ? EmptyString : ColonString
|
|
155
|
+
database = config.fetch('database', nil)
|
|
156
|
+
username = config.fetch('username', nil)
|
|
157
|
+
password = config.fetch('password', nil)
|
|
158
|
+
parameters = DefaultConnectionOptions.dup
|
|
159
|
+
parameters.merge!(user: username) unless username.nil?
|
|
160
|
+
parameters.merge!(password: password) unless password.nil? || password.empty?
|
|
161
|
+
parameter_values = parameters.map { |key, value| format(KeyValueTemplate, key: key, value: value) }
|
|
162
|
+
query = parameter_values.join(AmpersandString)
|
|
163
|
+
url = [format(URLTemplate, scheme: adapter, host: host, colon: colon, port: port)]
|
|
164
|
+
url << database unless database.nil? || database.empty?
|
|
165
|
+
url = [url.join(ForwardSlashString)]
|
|
166
|
+
url << query unless query.nil? || query.empty?
|
|
167
|
+
url.join(QuestionMarkString)
|
|
168
|
+
end
|
|
169
|
+
# rubocop: enable Metrics/AbcSize
|
|
170
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
171
|
+
# rubocop: enable Metrics/MethodLength
|
|
172
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
173
|
+
|
|
174
|
+
DatabaseOrRoleDoesNotExistPattern = /(database|role) ".*" does not exist/.freeze
|
|
175
|
+
|
|
176
|
+
# rubocop: disable Metrics/AbcSize
|
|
177
|
+
# rubocop: disable Metrics/MethodLength
|
|
178
|
+
def connect(database = config.fetch('database', nil), url = assemble_url, connection_options = {})
|
|
179
|
+
log.debug "Connecting to database #{sanitize_url(url)}" if defined? log
|
|
180
|
+
ConnectionAttempts.memo += 1
|
|
181
|
+
connection_options[:max_connections] = config.fetch('pool', DefaultConnectionPoolSize)
|
|
182
|
+
connection = Sequel.connect(url, **connection_options)
|
|
183
|
+
connection.extension(:connection_validator)
|
|
184
|
+
connection.pool.connection_validation_timeout = DefaultConnectionValidationTimeoutSeconds
|
|
185
|
+
connection.test_connection
|
|
186
|
+
log.debug "Connected to database #{database}" if defined? log
|
|
187
|
+
Sequel::Model.require_valid_table = false
|
|
188
|
+
Sequel::Model.db = connection
|
|
189
|
+
rescue Sequel::DatabaseConnectionError => e
|
|
190
|
+
raise if e.cause.is_a?(PG::ConnectionBad) && DatabaseOrRoleDoesNotExistPattern.match?(e.cause.to_s)
|
|
191
|
+
error_message = e.message.gsub(/Java::OrgPostgresqlUtil::PSQLException: /, '')
|
|
192
|
+
error_message = "Error connecting to database: #{error_message}"
|
|
193
|
+
log.error error_message
|
|
194
|
+
abort
|
|
195
|
+
rescue Sequel::AdapterNotFound => e
|
|
196
|
+
error_message = "Adapter not found: #{e.message}"
|
|
197
|
+
log.error error_message
|
|
198
|
+
abort
|
|
199
|
+
rescue StandardError => e
|
|
200
|
+
error_message = "Unexpected error connecting to database: #{e.message}"
|
|
201
|
+
log.error error_message, e
|
|
202
|
+
abort
|
|
203
|
+
end
|
|
204
|
+
# rubocop: enable Metrics/AbcSize
|
|
205
|
+
# rubocop: enable Metrics/MethodLength
|
|
206
|
+
|
|
207
|
+
def sanitize_url(url)
|
|
208
|
+
url.gsub(/&?(password|loggerLevel)=[^&]+&?/, '').gsub(/\?$/, '')
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
# module DatabaseConnectionHelpers
|
|
212
|
+
|
|
213
|
+
# The Databases module
|
|
214
|
+
module Databases
|
|
215
|
+
# The Databases::Management module
|
|
216
|
+
module Management
|
|
217
|
+
include DatabaseConnectionHelpers
|
|
218
|
+
|
|
219
|
+
# rubocop: disable Metrics/AbcSize
|
|
220
|
+
# rubocop: disable Metrics/MethodLength
|
|
221
|
+
def reconnect_database(url)
|
|
222
|
+
log.debug "Reconnecting to database #{sanitize_url(url)}"
|
|
223
|
+
begin
|
|
224
|
+
Sequel::Model.db&.disconnect
|
|
225
|
+
rescue StandardError => e
|
|
226
|
+
log.warn "Encountered unexpected error disconnecting from database: #{e.message}"
|
|
227
|
+
end
|
|
228
|
+
Sequel::Model.db = Sequel.connect(url)
|
|
229
|
+
Sequel::Model.descendants.each do |model|
|
|
230
|
+
model_dataset = begin
|
|
231
|
+
model.dataset
|
|
232
|
+
rescue Sequel::Error => e
|
|
233
|
+
nil
|
|
234
|
+
end
|
|
235
|
+
model.db = Sequel::Model.db if model_dataset.nil?
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
# rubocop: enable Metrics/AbcSize
|
|
239
|
+
# rubocop: enable Metrics/MethodLength
|
|
240
|
+
|
|
241
|
+
# rubocop: disable Metrics/AbcSize
|
|
242
|
+
# rubocop: disable Metrics/MethodLength
|
|
243
|
+
def create_database(database_name = database, username = database)
|
|
244
|
+
return false if database_exist?(database_name)
|
|
245
|
+
begin
|
|
246
|
+
log.info "Creating database: #{database_name}"
|
|
247
|
+
create_user username unless user_exist?(username)
|
|
248
|
+
run "create database #{database_name} owner #{username}"
|
|
249
|
+
rescue StandardError => e
|
|
250
|
+
database_already_exists_error_pattern = /ERROR: database "#{database_name}" already exists/
|
|
251
|
+
if database_already_exists_error_pattern.match?(e.to_s)
|
|
252
|
+
unless defined? tried_once_already
|
|
253
|
+
drop_database database_name
|
|
254
|
+
tried_once_already = true # rubocop: disable Lint/UselessAssignment
|
|
255
|
+
retry
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
log.error "Unexpected error creating database: #{e.inspect}"
|
|
259
|
+
e.backtrace.each { |t| log.error t }
|
|
260
|
+
return false
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
# rubocop: enable Metrics/AbcSize
|
|
264
|
+
# rubocop: enable Metrics/MethodLength
|
|
265
|
+
|
|
266
|
+
CannotDropOpenDatabaseErrorPattern = /ERROR: cannot drop the currently open database/.freeze
|
|
267
|
+
|
|
268
|
+
# rubocop: disable Metrics/AbcSize
|
|
269
|
+
# rubocop: disable Metrics/MethodLength
|
|
270
|
+
def drop_database(database_name = database)
|
|
271
|
+
return false unless database_exist?(database_name)
|
|
272
|
+
begin
|
|
273
|
+
log.info "Dropping database: #{database_name}"
|
|
274
|
+
run "drop database #{database_name}"
|
|
275
|
+
rescue Sequel::DatabaseError => e
|
|
276
|
+
log.error "Unexpected error creating database: #{e.inspect}"
|
|
277
|
+
e.backtrace.each { |t| log.error t }
|
|
278
|
+
return false
|
|
279
|
+
rescue StandardError => e
|
|
280
|
+
if CannotDropOpenDatabaseErrorPattern.match?(e.to_s)
|
|
281
|
+
results = execute "select procpid from pg_stat_activity where datname = '#{database_name}'"
|
|
282
|
+
results.each do |row|
|
|
283
|
+
log.debug "You must first kill process with pid #{row['procpid']}"
|
|
284
|
+
command = format('sudo kill -9 %<procpid>s', procpid: row['procpid'])
|
|
285
|
+
log.debug "Execute: #{command}"
|
|
286
|
+
# `#{command}`
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
log.error "Unexpected error creating database: #{e.inspect}"
|
|
290
|
+
e.backtrace.each { |t| log.error t }
|
|
291
|
+
return false
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
# rubocop: enable Metrics/AbcSize
|
|
295
|
+
# rubocop: enable Metrics/MethodLength
|
|
296
|
+
end
|
|
297
|
+
# module Management
|
|
298
|
+
end
|
|
299
|
+
# module Databases
|
|
300
|
+
|
|
301
|
+
# The Databases module
|
|
302
|
+
module Databases
|
|
303
|
+
# The Utilities module
|
|
304
|
+
module Utilities
|
|
305
|
+
DatabaseURITemplate = '%<schema>s://%<host>s%<port>s/%<database_name>s?' \
|
|
306
|
+
'user=%<username>s&password=%<password>s&loggerLevel=%<logger_level>s'.freeze
|
|
307
|
+
|
|
308
|
+
# rubocop: disable Metrics/AbcSize
|
|
309
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
310
|
+
# rubocop: disable Metrics/MethodLength
|
|
311
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
312
|
+
def switch_database(database_name = database)
|
|
313
|
+
uri = Sequel::Model.db.opts[:uri] || ''
|
|
314
|
+
schema = uri.to_s.scan(/^(\w+(:\w+)?):\/\//).first&.first || 'jdbc:postgresql'
|
|
315
|
+
host = uri.to_s.scan(/:\/\/([\w.\d]+)/).first&.first || localhost
|
|
316
|
+
port = uri.to_s.scan(/:\/\/\w+(:(\d+))?/).first&.first
|
|
317
|
+
username = database_name || uri.to_s.scan(/user=([^&]+)/).first
|
|
318
|
+
password = uri.to_s.scan(/password=([^&]+)/).first&.first || username
|
|
319
|
+
logger_level = 'OFF'
|
|
320
|
+
url = format(DatabaseURITemplate,
|
|
321
|
+
schema: schema,
|
|
322
|
+
host: host,
|
|
323
|
+
port: port.nil? || port.empty? ? '' : ':' + port.to_s,
|
|
324
|
+
database_name: database_name,
|
|
325
|
+
username: username,
|
|
326
|
+
password: password,
|
|
327
|
+
logger_level: logger_level
|
|
328
|
+
)
|
|
329
|
+
reconnect_database(url)
|
|
330
|
+
return true
|
|
331
|
+
end
|
|
332
|
+
# rubocop: enable Metrics/AbcSize
|
|
333
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
334
|
+
# rubocop: enable Metrics/MethodLength
|
|
335
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
336
|
+
|
|
337
|
+
# rubocop: disable Metrics/AbcSize
|
|
338
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
339
|
+
# rubocop: disable Metrics/MethodLength
|
|
340
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
341
|
+
def switch_user(username = database, database_name = nil)
|
|
342
|
+
uri = Sequel::Model.db.opts[:uri] || ''
|
|
343
|
+
schema = uri.to_s.scan(/^(\w+(:\w+)?):\/\//).first&.first || 'jdbc:postgresql'
|
|
344
|
+
host = uri.to_s.scan(/:\/\/([\w.\d]+)/).first&.first || localhost
|
|
345
|
+
port = uri.to_s.scan(/:\/\/\w+(:(\d+))?/).first&.first
|
|
346
|
+
database_name = database_name || uri.to_s.scan(/\/(\w+)\??(\w+=\w+)*$/).first || database
|
|
347
|
+
password = uri.to_s.scan(/password=([^&]+)/).first&.first || username
|
|
348
|
+
logger_level = 'OFF'
|
|
349
|
+
url = format(DatabaseURITemplate,
|
|
350
|
+
schema: schema,
|
|
351
|
+
host: host,
|
|
352
|
+
port: port.nil? || port.empty? ? '' : ':' + port.to_s,
|
|
353
|
+
database_name: database_name,
|
|
354
|
+
username: username,
|
|
355
|
+
password: password,
|
|
356
|
+
logger_level: logger_level
|
|
357
|
+
)
|
|
358
|
+
reconnect_database(url)
|
|
359
|
+
return true
|
|
360
|
+
end
|
|
361
|
+
# rubocop: enable Metrics/AbcSize
|
|
362
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
363
|
+
# rubocop: enable Metrics/MethodLength
|
|
364
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
365
|
+
|
|
366
|
+
# rubocop: disable Metrics/MethodLength
|
|
367
|
+
def create_user(username = database)
|
|
368
|
+
return false if user_exist?(username)
|
|
369
|
+
begin
|
|
370
|
+
log.debug "Creating user: #{username}"
|
|
371
|
+
run "create user #{username}"
|
|
372
|
+
run "alter role #{username} with password '#{username}'"
|
|
373
|
+
return true
|
|
374
|
+
rescue Sequel::DatabaseError => e
|
|
375
|
+
log.error e.inspect
|
|
376
|
+
return false
|
|
377
|
+
rescue StandardError => e
|
|
378
|
+
log.error e.inspect
|
|
379
|
+
return false
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
# rubocop: enable Metrics/MethodLength
|
|
383
|
+
|
|
384
|
+
# rubocop: disable Metrics/MethodLength
|
|
385
|
+
def delete_user(username = database)
|
|
386
|
+
return false unless user_exist?(username)
|
|
387
|
+
begin
|
|
388
|
+
log.debug "Dropping user: #{username}"
|
|
389
|
+
run "reassign owned by #{username} to postgres"
|
|
390
|
+
run "drop user #{username}"
|
|
391
|
+
rescue Sequel::DatabaseError => e
|
|
392
|
+
log.error e.inspect
|
|
393
|
+
return false
|
|
394
|
+
rescue StandardError => e
|
|
395
|
+
log.error e.inspect
|
|
396
|
+
return false
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
# rubocop: enable Metrics/MethodLength
|
|
400
|
+
end
|
|
401
|
+
# module Utilities
|
|
402
|
+
end
|
|
403
|
+
# module Databases
|
|
404
|
+
|
|
405
|
+
# The Sequel module
|
|
406
|
+
module Sequel
|
|
407
|
+
# The Sequel::Postgres module
|
|
408
|
+
module Postgres
|
|
409
|
+
if defined?(Sequel::Migration)
|
|
410
|
+
# The Sequel::Postgres::Bootstrap class
|
|
411
|
+
class Bootstrap < Sequel::Migration
|
|
412
|
+
include Sequel::Inflections
|
|
413
|
+
include Databases::Helpers
|
|
414
|
+
include Databases::Management
|
|
415
|
+
include Databases::Utilities
|
|
416
|
+
|
|
417
|
+
def up
|
|
418
|
+
create_user
|
|
419
|
+
create_database
|
|
420
|
+
switch_database
|
|
421
|
+
switch_user
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
def down
|
|
425
|
+
switch_database :postgres
|
|
426
|
+
drop_database
|
|
427
|
+
delete_user
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def self.migrate
|
|
431
|
+
MigrationMethods.each do |method_name|
|
|
432
|
+
Inform::Databases.instances.each_value { |db| db.send(method_name) }
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def self.up
|
|
437
|
+
Inform::Databases.instances.each_value { |db| db.send(:up) }
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
# class Bootstrap
|
|
441
|
+
end
|
|
442
|
+
# defined?(Sequel::Migration)
|
|
443
|
+
end
|
|
444
|
+
# module Postgres
|
|
445
|
+
end
|
|
446
|
+
# module Sequel
|
|
447
|
+
|
|
448
|
+
# The Inform module
|
|
449
|
+
module Inform
|
|
450
|
+
# The Databases module
|
|
451
|
+
module Databases
|
|
452
|
+
@instances = {}
|
|
453
|
+
def instances
|
|
454
|
+
@instances
|
|
455
|
+
end
|
|
456
|
+
module_function :instances
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
if defined?(Sequel::Postgres::Bootstrap)
|
|
461
|
+
# The Inform module
|
|
462
|
+
module Inform
|
|
463
|
+
# The Database abstraction class
|
|
464
|
+
class Database
|
|
465
|
+
attr_reader :bootstrap
|
|
466
|
+
|
|
467
|
+
def self.init(database_name)
|
|
468
|
+
Inform::Database.instance(database_name)
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
@instance_mutex = Mutex.new
|
|
472
|
+
|
|
473
|
+
def self.instance(*args)
|
|
474
|
+
return @instance unless @instance.nil?
|
|
475
|
+
@instance_mutex.synchronize do
|
|
476
|
+
@instance ||= new(*args)
|
|
477
|
+
end
|
|
478
|
+
@instance
|
|
479
|
+
end
|
|
480
|
+
|
|
481
|
+
private_class_method :new
|
|
482
|
+
|
|
483
|
+
def initialize(env = Game.environment)
|
|
484
|
+
log.debug "Initializing persistence layer for environment: #{env}"
|
|
485
|
+
log.debug caller[0..4]
|
|
486
|
+
env ||= environment
|
|
487
|
+
@config = database_config.fetch(env.to_s, {})
|
|
488
|
+
establish_database_connection
|
|
489
|
+
enable_plugins
|
|
490
|
+
end
|
|
491
|
+
REGISTRY = Struct.new(:memo).new(defined?(Java) ? java.util.concurrent.ConcurrentHashMap.new : {})
|
|
492
|
+
def initialize(database_name)
|
|
493
|
+
@bootstrap = Sequel::Postgres::Bootstrap.new(Sequel::Model.db)
|
|
494
|
+
Inform::Databases.instances[database_name] = self
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
# module Inform
|
|
499
|
+
end
|
|
500
|
+
# defined?(Sequel::Postgres::Bootstrap)
|