tormenta20 0.2.2 → 0.2.3
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/db/seeds.rb +2 -1
- data/db/tormenta20.sqlite3 +0 -0
- data/exe/tormenta20 +377 -0
- data/src/ruby/tormenta20/database.rb +78 -142
- data/src/ruby/tormenta20/version.rb +1 -1
- data/src/ruby/tormenta20.rb +7 -11
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8bda7c785ab5b22dd5d1652986dc37b62f7b3fa59b2d4690185740c3c4aac33d
|
|
4
|
+
data.tar.gz: feb865c7dee191eb5dcff058b2a0b9071a3415c3fa5579706f603c58402dcc68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f6e33daca420c543a2d87d9b1f1ecc0d9497780f9e59db41f3ec15a8f60a439e0eb43b032eeead981c67c4c928d64ec3f624b076ffac27e7aac9c1210baec91d
|
|
7
|
+
data.tar.gz: 3f4b898a1332a1d6c2e34cab772ceb124fc8b7ae31e420683c7b7960cc34ab8ac62df5c6d8270b8df6a426611cb087bf314b34311f8b9f63df4e03d046cf1e8d
|
data/db/seeds.rb
CHANGED
|
@@ -372,6 +372,7 @@ end
|
|
|
372
372
|
|
|
373
373
|
# Run seeds if this file is executed directly
|
|
374
374
|
if __FILE__ == $PROGRAM_NAME
|
|
375
|
-
|
|
375
|
+
ENV["TORMENTA20_DB_MODE"] = "create_on_build"
|
|
376
|
+
Tormenta20::Database.setup
|
|
376
377
|
Tormenta20::Seeds.import_all
|
|
377
378
|
end
|
data/db/tormenta20.sqlite3
CHANGED
|
Binary file
|
data/exe/tormenta20
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
# CLI for managing the Tormenta20 database
|
|
8
|
+
module Tormenta20CLI
|
|
9
|
+
class << self
|
|
10
|
+
def run(args)
|
|
11
|
+
options = parse_options(args)
|
|
12
|
+
command = args.shift || "help"
|
|
13
|
+
|
|
14
|
+
case command
|
|
15
|
+
when "build"
|
|
16
|
+
build_database(options)
|
|
17
|
+
when "seed"
|
|
18
|
+
seed_data(options)
|
|
19
|
+
when "reset"
|
|
20
|
+
reset_database(options)
|
|
21
|
+
when "info"
|
|
22
|
+
show_info(options)
|
|
23
|
+
when "export"
|
|
24
|
+
export_json(options)
|
|
25
|
+
when "import"
|
|
26
|
+
import_json(options)
|
|
27
|
+
when "help", "--help", "-h"
|
|
28
|
+
show_help
|
|
29
|
+
else
|
|
30
|
+
puts "Unknown command: #{command}"
|
|
31
|
+
show_help
|
|
32
|
+
exit 1
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def parse_options(args)
|
|
39
|
+
options = {}
|
|
40
|
+
|
|
41
|
+
OptionParser.new do |opts|
|
|
42
|
+
opts.banner = "Usage: tormenta20 <command> [options]"
|
|
43
|
+
|
|
44
|
+
opts.on("-p", "--path PATH", "Database path") do |path|
|
|
45
|
+
options[:path] = path
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
opts.on("-t", "--tables TABLES", "Comma-separated tables to seed") do |tables|
|
|
49
|
+
options[:tables] = tables.split(",").map(&:strip)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts.on("-j", "--json PATH", "JSON file or directory path") do |path|
|
|
53
|
+
options[:json_path] = path
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
opts.on("-o", "--output PATH", "Output path for export") do |path|
|
|
57
|
+
options[:output] = path
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
opts.on("-v", "--verbose", "Verbose output") do
|
|
61
|
+
options[:verbose] = true
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
opts.on("-f", "--force", "Force overwrite") do
|
|
65
|
+
options[:force] = true
|
|
66
|
+
end
|
|
67
|
+
end.parse!(args)
|
|
68
|
+
|
|
69
|
+
options
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def setup_env(options)
|
|
73
|
+
if options[:path]
|
|
74
|
+
ENV["TORMENTA20_DB_MODE"] = "path"
|
|
75
|
+
ENV["TORMENTA20_DB_PATH"] = File.expand_path(options[:path])
|
|
76
|
+
else
|
|
77
|
+
ENV["TORMENTA20_DB_MODE"] = "create_on_build"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
require "tormenta20"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_database(options)
|
|
84
|
+
puts "Building database..."
|
|
85
|
+
|
|
86
|
+
if options[:path]
|
|
87
|
+
path = File.expand_path(options[:path])
|
|
88
|
+
FileUtils.rm_f(path) if options[:force] && File.exist?(path)
|
|
89
|
+
|
|
90
|
+
if File.exist?(path) && !options[:force]
|
|
91
|
+
puts "Database already exists at #{path}"
|
|
92
|
+
puts "Use --force to overwrite"
|
|
93
|
+
exit 1
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
ENV["TORMENTA20_DB_MODE"] = "path"
|
|
97
|
+
ENV["TORMENTA20_DB_PATH"] = path
|
|
98
|
+
else
|
|
99
|
+
ENV["TORMENTA20_DB_MODE"] = "create_on_build"
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
require "tormenta20"
|
|
103
|
+
|
|
104
|
+
# Force rebuild
|
|
105
|
+
Tormenta20::Database.disconnect if Tormenta20::Database.connected?
|
|
106
|
+
FileUtils.rm_f(Tormenta20::Database.db_path) if options[:force]
|
|
107
|
+
|
|
108
|
+
Tormenta20::Database.send(:ensure_database_exists)
|
|
109
|
+
Tormenta20::Database.send(:connect_to_database)
|
|
110
|
+
|
|
111
|
+
puts "Database built at: #{Tormenta20::Database.db_path}"
|
|
112
|
+
show_summary
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def seed_data(options)
|
|
116
|
+
setup_env(options)
|
|
117
|
+
|
|
118
|
+
tables = options[:tables] || all_tables
|
|
119
|
+
|
|
120
|
+
puts "Seeding tables: #{tables.join(", ")}"
|
|
121
|
+
|
|
122
|
+
tables.each do |table|
|
|
123
|
+
seed_table(table, options)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
show_summary
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def reset_database(options)
|
|
130
|
+
setup_env(options)
|
|
131
|
+
|
|
132
|
+
puts "Resetting database..."
|
|
133
|
+
Tormenta20::Database.reset
|
|
134
|
+
puts "Database reset complete."
|
|
135
|
+
show_summary
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def show_info(options)
|
|
139
|
+
setup_env(options)
|
|
140
|
+
|
|
141
|
+
puts "Tormenta20 Database Info"
|
|
142
|
+
puts "=" * 40
|
|
143
|
+
puts "Mode: #{Tormenta20::Database.mode}"
|
|
144
|
+
puts "Path: #{Tormenta20::Database.db_path}"
|
|
145
|
+
puts "Connected: #{Tormenta20::Database.connected?}"
|
|
146
|
+
puts ""
|
|
147
|
+
show_summary
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def export_json(options)
|
|
151
|
+
setup_env(options)
|
|
152
|
+
|
|
153
|
+
output_dir = options[:output] || "./export"
|
|
154
|
+
tables = options[:tables] || all_tables
|
|
155
|
+
|
|
156
|
+
FileUtils.mkdir_p(output_dir)
|
|
157
|
+
|
|
158
|
+
tables.each do |table|
|
|
159
|
+
export_table(table, output_dir, options)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
puts "Exported to: #{output_dir}"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def import_json(options)
|
|
166
|
+
setup_env(options)
|
|
167
|
+
|
|
168
|
+
json_path = options[:json_path]
|
|
169
|
+
unless json_path
|
|
170
|
+
puts "Error: --json PATH is required"
|
|
171
|
+
exit 1
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
if File.directory?(json_path)
|
|
175
|
+
import_directory(json_path, options)
|
|
176
|
+
elsif File.file?(json_path)
|
|
177
|
+
import_file(json_path, options)
|
|
178
|
+
else
|
|
179
|
+
puts "Error: Path not found: #{json_path}"
|
|
180
|
+
exit 1
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def show_help
|
|
185
|
+
puts <<~HELP
|
|
186
|
+
Tormenta20 Database CLI
|
|
187
|
+
|
|
188
|
+
Usage: tormenta20 <command> [options]
|
|
189
|
+
|
|
190
|
+
Commands:
|
|
191
|
+
build Build/rebuild the database from JSON files
|
|
192
|
+
seed Seed specific tables
|
|
193
|
+
reset Reset the database (delete and rebuild)
|
|
194
|
+
info Show database information
|
|
195
|
+
export Export data to JSON files
|
|
196
|
+
import Import JSON file(s) into the database
|
|
197
|
+
help Show this help
|
|
198
|
+
|
|
199
|
+
Options:
|
|
200
|
+
-p, --path PATH Custom database path
|
|
201
|
+
-t, --tables TABLES Comma-separated tables (origens,classes,magias,...)
|
|
202
|
+
-j, --json PATH JSON file or directory to import
|
|
203
|
+
-o, --output PATH Output directory for export
|
|
204
|
+
-v, --verbose Verbose output
|
|
205
|
+
-f, --force Force overwrite existing files
|
|
206
|
+
|
|
207
|
+
Tables:
|
|
208
|
+
origens, poderes, divindades, classes, magias,
|
|
209
|
+
armas, armaduras, escudos, itens,
|
|
210
|
+
materiais_especiais, melhorias, regras
|
|
211
|
+
|
|
212
|
+
Examples:
|
|
213
|
+
tormenta20 build # Build with default path
|
|
214
|
+
tormenta20 build -p ./my.db -f # Build at custom path
|
|
215
|
+
tormenta20 seed -t magias,classes # Seed only specific tables
|
|
216
|
+
tormenta20 import -j ./custom_spell.json # Import a JSON file
|
|
217
|
+
tormenta20 export -o ./backup # Export all to JSON
|
|
218
|
+
tormenta20 info # Show database info
|
|
219
|
+
HELP
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def all_tables
|
|
223
|
+
%w[origens poderes divindades classes magias armas armaduras escudos itens materiais_especiais melhorias regras]
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def seed_table(table, options)
|
|
227
|
+
print " Seeding #{table}..."
|
|
228
|
+
|
|
229
|
+
case table
|
|
230
|
+
when "origens"
|
|
231
|
+
Tormenta20::Seeder.send(:seed_origens)
|
|
232
|
+
when "poderes"
|
|
233
|
+
Tormenta20::Seeder.send(:seed_poderes_habilidades_unicas)
|
|
234
|
+
Tormenta20::Seeder.send(:seed_poderes_concedidos)
|
|
235
|
+
Tormenta20::Seeder.send(:seed_poderes_tormenta)
|
|
236
|
+
when "divindades"
|
|
237
|
+
Tormenta20::Seeder.send(:seed_divindades)
|
|
238
|
+
when "classes"
|
|
239
|
+
Tormenta20::Seeder.send(:seed_classes)
|
|
240
|
+
when "magias"
|
|
241
|
+
Tormenta20::Seeder.send(:seed_magias)
|
|
242
|
+
when "armas"
|
|
243
|
+
Tormenta20::Seeder.send(:seed_armas)
|
|
244
|
+
when "armaduras"
|
|
245
|
+
Tormenta20::Seeder.send(:seed_armaduras)
|
|
246
|
+
when "escudos"
|
|
247
|
+
Tormenta20::Seeder.send(:seed_escudos)
|
|
248
|
+
when "itens"
|
|
249
|
+
Tormenta20::Seeder.send(:seed_itens)
|
|
250
|
+
when "materiais_especiais"
|
|
251
|
+
Tormenta20::Seeder.send(:seed_materiais_especiais)
|
|
252
|
+
when "melhorias"
|
|
253
|
+
Tormenta20::Seeder.send(:seed_melhorias)
|
|
254
|
+
when "regras"
|
|
255
|
+
Tormenta20::Seeder.send(:seed_regras)
|
|
256
|
+
else
|
|
257
|
+
puts " unknown table"
|
|
258
|
+
return
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
puts " done"
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def export_table(table, output_dir, options)
|
|
265
|
+
model = table_to_model(table)
|
|
266
|
+
return unless model
|
|
267
|
+
|
|
268
|
+
table_dir = File.join(output_dir, table)
|
|
269
|
+
FileUtils.mkdir_p(table_dir)
|
|
270
|
+
|
|
271
|
+
count = 0
|
|
272
|
+
model.find_each do |record|
|
|
273
|
+
file_path = File.join(table_dir, "#{record.id}.json")
|
|
274
|
+
File.write(file_path, JSON.pretty_generate(record.to_h))
|
|
275
|
+
count += 1
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
puts " Exported #{count} #{table}" if options[:verbose]
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def import_directory(dir_path, options)
|
|
282
|
+
Dir.glob(File.join(dir_path, "**/*.json")).each do |file|
|
|
283
|
+
import_file(file, options)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def import_file(file_path, options)
|
|
288
|
+
data = JSON.parse(File.read(file_path), symbolize_names: true)
|
|
289
|
+
|
|
290
|
+
# Try to determine the table from the file path or data
|
|
291
|
+
table = detect_table(file_path, data)
|
|
292
|
+
model = table_to_model(table)
|
|
293
|
+
|
|
294
|
+
unless model
|
|
295
|
+
puts " Skipping #{file_path}: unknown table"
|
|
296
|
+
return
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
record = model.find_or_initialize_by(id: data[:id])
|
|
300
|
+
data.each do |key, value|
|
|
301
|
+
record.send("#{key}=", value) if record.respond_to?("#{key}=")
|
|
302
|
+
end
|
|
303
|
+
record.save!
|
|
304
|
+
|
|
305
|
+
puts " Imported #{data[:id]} into #{table}" if options[:verbose]
|
|
306
|
+
rescue StandardError => e
|
|
307
|
+
puts " Error importing #{file_path}: #{e.message}"
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def detect_table(file_path, data)
|
|
311
|
+
# Try to detect from path
|
|
312
|
+
path_parts = file_path.split("/")
|
|
313
|
+
possible_tables = all_tables
|
|
314
|
+
|
|
315
|
+
path_parts.each do |part|
|
|
316
|
+
return part if possible_tables.include?(part)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Try to detect from data structure
|
|
320
|
+
return "magias" if data[:circle] && data[:school]
|
|
321
|
+
return "classes" if data[:hit_points] && data[:abilities]
|
|
322
|
+
return "divindades" if data[:energy] && data[:granted_powers]
|
|
323
|
+
return "origens" if data[:benefits] && data[:unique_power]
|
|
324
|
+
return "armas" if data[:damage] && data[:critical]
|
|
325
|
+
return "armaduras" if data[:defense_bonus] && data[:category]
|
|
326
|
+
return "escudos" if data[:defense_bonus] && !data[:category]
|
|
327
|
+
|
|
328
|
+
nil
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
def table_to_model(table)
|
|
332
|
+
case table
|
|
333
|
+
when "origens" then Tormenta20::Models::Origem
|
|
334
|
+
when "poderes" then Tormenta20::Models::Poder
|
|
335
|
+
when "divindades" then Tormenta20::Models::Divindade
|
|
336
|
+
when "classes" then Tormenta20::Models::Classe
|
|
337
|
+
when "magias" then Tormenta20::Models::Magia
|
|
338
|
+
when "armas" then Tormenta20::Models::Arma
|
|
339
|
+
when "armaduras" then Tormenta20::Models::Armadura
|
|
340
|
+
when "escudos" then Tormenta20::Models::Escudo
|
|
341
|
+
when "itens" then Tormenta20::Models::Item
|
|
342
|
+
when "materiais_especiais" then Tormenta20::Models::MaterialEspecial
|
|
343
|
+
when "melhorias" then Tormenta20::Models::Melhoria
|
|
344
|
+
when "regras" then Tormenta20::Models::Regra
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def show_summary
|
|
349
|
+
puts ""
|
|
350
|
+
puts "Database Summary:"
|
|
351
|
+
puts "-" * 40
|
|
352
|
+
|
|
353
|
+
{
|
|
354
|
+
"Origens" => Tormenta20::Models::Origem,
|
|
355
|
+
"Poderes" => Tormenta20::Models::Poder,
|
|
356
|
+
"Divindades" => Tormenta20::Models::Divindade,
|
|
357
|
+
"Classes" => Tormenta20::Models::Classe,
|
|
358
|
+
"Magias" => Tormenta20::Models::Magia,
|
|
359
|
+
"Armas" => Tormenta20::Models::Arma,
|
|
360
|
+
"Armaduras" => Tormenta20::Models::Armadura,
|
|
361
|
+
"Escudos" => Tormenta20::Models::Escudo,
|
|
362
|
+
"Itens" => Tormenta20::Models::Item,
|
|
363
|
+
"Materiais Especiais" => Tormenta20::Models::MaterialEspecial,
|
|
364
|
+
"Melhorias" => Tormenta20::Models::Melhoria,
|
|
365
|
+
"Regras" => Tormenta20::Models::Regra
|
|
366
|
+
}.each do |name, model|
|
|
367
|
+
printf " %-20s %d\n", "#{name}:", model.count
|
|
368
|
+
rescue StandardError
|
|
369
|
+
printf " %-20s %s\n", "#{name}:", "N/A"
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
puts "-" * 40
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
Tormenta20CLI.run(ARGV)
|
|
@@ -7,89 +7,52 @@ require "fileutils"
|
|
|
7
7
|
module Tormenta20
|
|
8
8
|
# Database connection and setup management for Tormenta20.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
10
|
+
# Configuration via environment variables:
|
|
11
|
+
# - TORMENTA20_DB_MODE: "built_in" (default), "create_on_build", or "path"
|
|
12
|
+
# - TORMENTA20_DB_PATH: Custom path (required when mode is "path")
|
|
12
13
|
#
|
|
13
|
-
# @example
|
|
14
|
-
#
|
|
14
|
+
# @example Using built-in database (default)
|
|
15
|
+
# # No configuration needed, uses pre-built database from gem
|
|
16
|
+
# require 'tormenta20'
|
|
15
17
|
#
|
|
16
|
-
# @example
|
|
17
|
-
#
|
|
18
|
+
# @example Creating database on build
|
|
19
|
+
# # Set before requiring the gem:
|
|
20
|
+
# # TORMENTA20_DB_MODE=create_on_build
|
|
21
|
+
#
|
|
22
|
+
# @example Using custom path
|
|
23
|
+
# # TORMENTA20_DB_MODE=path
|
|
24
|
+
# # TORMENTA20_DB_PATH=/my/custom/path.sqlite3
|
|
18
25
|
module Database
|
|
26
|
+
# Valid database modes
|
|
27
|
+
MODES = %w[built_in create_on_build path].freeze
|
|
28
|
+
|
|
19
29
|
class << self
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
# @return [String] Current database mode
|
|
31
|
+
def mode
|
|
32
|
+
@mode ||= ENV.fetch("TORMENTA20_DB_MODE", "built_in")
|
|
33
|
+
end
|
|
23
34
|
|
|
24
|
-
#
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
# @return [String] Path to the SQLite database file
|
|
36
|
+
def db_path
|
|
37
|
+
@db_path ||= resolve_db_path
|
|
38
|
+
end
|
|
27
39
|
|
|
28
40
|
# Initialize and configure the database connection.
|
|
29
41
|
#
|
|
30
|
-
# @param mode [Symbol] Database initialization mode:
|
|
31
|
-
# - `:builtin` - Use pre-built database (connects immediately)
|
|
32
|
-
# - `:build` - Build database on load (creates if missing, then connects)
|
|
33
|
-
# - `:lazy` - Build database on first use (default)
|
|
34
|
-
# @param db_path [String, nil] Custom path to SQLite database file.
|
|
35
|
-
# If nil, uses the default path.
|
|
36
42
|
# @return [void]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
# @example Default lazy setup
|
|
40
|
-
# Database.setup
|
|
41
|
-
#
|
|
42
|
-
# @example Immediate connection with builtin database
|
|
43
|
-
# Database.setup(mode: :builtin)
|
|
44
|
-
def setup(mode: :lazy, db_path: nil)
|
|
45
|
-
@mode = mode
|
|
46
|
-
@db_path = db_path || default_db_path
|
|
43
|
+
def setup
|
|
44
|
+
validate_mode!
|
|
47
45
|
@setup_done = true
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
connect_to_database
|
|
52
|
-
when :build
|
|
53
|
-
ensure_database_exists
|
|
54
|
-
connect_to_database
|
|
55
|
-
when :lazy
|
|
56
|
-
# Connection will be established on first use via ensure_connected
|
|
57
|
-
nil
|
|
58
|
-
else
|
|
59
|
-
raise ArgumentError, "Invalid mode: #{mode}. Use :builtin, :build, or :lazy"
|
|
60
|
-
end
|
|
47
|
+
ensure_database_exists if mode == "create_on_build"
|
|
48
|
+
connect_to_database
|
|
61
49
|
end
|
|
62
50
|
|
|
63
51
|
# Ensure database is connected, setting up if needed.
|
|
64
52
|
#
|
|
65
|
-
# This method is called automatically when models are accessed.
|
|
66
|
-
# It handles lazy initialization of the database connection.
|
|
67
|
-
#
|
|
68
53
|
# @return [void]
|
|
69
54
|
def ensure_connected
|
|
70
55
|
setup unless @setup_done
|
|
71
|
-
connect_to_database unless connected?
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Establish connection to the SQLite database.
|
|
75
|
-
#
|
|
76
|
-
# Creates the database if it doesn't exist (in lazy mode) and
|
|
77
|
-
# establishes an ActiveRecord connection.
|
|
78
|
-
#
|
|
79
|
-
# @return [void]
|
|
80
|
-
def connect_to_database
|
|
81
|
-
return if connected?
|
|
82
|
-
|
|
83
|
-
ensure_database_exists if mode == :lazy
|
|
84
|
-
|
|
85
|
-
ActiveRecord::Base.establish_connection(
|
|
86
|
-
adapter: "sqlite3",
|
|
87
|
-
database: db_path,
|
|
88
|
-
pool: 5,
|
|
89
|
-
timeout: 5000
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
@connected = true
|
|
93
56
|
end
|
|
94
57
|
|
|
95
58
|
# Check if database connection is active.
|
|
@@ -101,62 +64,12 @@ module Tormenta20
|
|
|
101
64
|
false
|
|
102
65
|
end
|
|
103
66
|
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
# Creates the database directory, file, and runs the schema
|
|
107
|
-
# if the database doesn't exist.
|
|
108
|
-
#
|
|
109
|
-
# @return [void]
|
|
110
|
-
def ensure_database_exists
|
|
111
|
-
db_dir = File.dirname(db_path)
|
|
112
|
-
FileUtils.mkdir_p(db_dir) unless Dir.exist?(db_dir)
|
|
113
|
-
|
|
114
|
-
needs_seed = !File.exist?(db_path)
|
|
115
|
-
|
|
116
|
-
return unless needs_seed
|
|
117
|
-
|
|
118
|
-
# Create empty database
|
|
119
|
-
SQLite3::Database.new(db_path).close
|
|
120
|
-
|
|
121
|
-
# Run schema if available
|
|
122
|
-
run_schema if schema_exists?
|
|
123
|
-
|
|
124
|
-
# Seed data after schema creation
|
|
125
|
-
seed_database
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Seed database with data from JSON files.
|
|
129
|
-
#
|
|
130
|
-
# @return [void]
|
|
131
|
-
def seed_database
|
|
132
|
-
return if Seeder.seeded?
|
|
133
|
-
|
|
134
|
-
Seeder.seed_all(verbose: false)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Run pending database migrations.
|
|
138
|
-
#
|
|
139
|
-
# @return [void]
|
|
140
|
-
def migrate
|
|
141
|
-
connect_to_database
|
|
142
|
-
ActiveRecord::MigrationContext.new(migrations_path, ActiveRecord::SchemaMigration).migrate
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Rollback database migrations.
|
|
67
|
+
# Disconnect from the database.
|
|
146
68
|
#
|
|
147
|
-
# @param steps [Integer] Number of migrations to rollback
|
|
148
69
|
# @return [void]
|
|
149
|
-
def
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# Get current database migration version.
|
|
155
|
-
#
|
|
156
|
-
# @return [Integer] Current migration version number
|
|
157
|
-
def version
|
|
158
|
-
connect_to_database
|
|
159
|
-
ActiveRecord::Base.connection.migration_context.current_version
|
|
70
|
+
def disconnect
|
|
71
|
+
ActiveRecord::Base.remove_connection if connected?
|
|
72
|
+
@connected = false
|
|
160
73
|
end
|
|
161
74
|
|
|
162
75
|
# Reset database by dropping and recreating it.
|
|
@@ -170,14 +83,6 @@ module Tormenta20
|
|
|
170
83
|
connect_to_database
|
|
171
84
|
end
|
|
172
85
|
|
|
173
|
-
# Disconnect from the database.
|
|
174
|
-
#
|
|
175
|
-
# @return [void]
|
|
176
|
-
def disconnect
|
|
177
|
-
ActiveRecord::Base.remove_connection if connected?
|
|
178
|
-
@connected = false
|
|
179
|
-
end
|
|
180
|
-
|
|
181
86
|
# Get the default database file path.
|
|
182
87
|
#
|
|
183
88
|
# @return [String] Absolute path to the default database file
|
|
@@ -192,31 +97,62 @@ module Tormenta20
|
|
|
192
97
|
File.expand_path("../../../db/schema.sql", __dir__)
|
|
193
98
|
end
|
|
194
99
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def resolve_db_path
|
|
103
|
+
case mode
|
|
104
|
+
when "path"
|
|
105
|
+
ENV.fetch("TORMENTA20_DB_PATH") do
|
|
106
|
+
raise ArgumentError, "TORMENTA20_DB_PATH is required when TORMENTA20_DB_MODE=path"
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
default_db_path
|
|
110
|
+
end
|
|
200
111
|
end
|
|
201
112
|
|
|
202
|
-
|
|
113
|
+
def validate_mode!
|
|
114
|
+
return if MODES.include?(mode)
|
|
115
|
+
|
|
116
|
+
raise ArgumentError, "Invalid TORMENTA20_DB_MODE: #{mode}. Valid: #{MODES.join(", ")}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def connect_to_database
|
|
120
|
+
return if connected?
|
|
121
|
+
|
|
122
|
+
ActiveRecord::Base.establish_connection(
|
|
123
|
+
adapter: "sqlite3",
|
|
124
|
+
database: db_path,
|
|
125
|
+
pool: 5,
|
|
126
|
+
timeout: 5000
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@connected = true
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def ensure_database_exists
|
|
133
|
+
db_dir = File.dirname(db_path)
|
|
134
|
+
FileUtils.mkdir_p(db_dir) unless Dir.exist?(db_dir)
|
|
135
|
+
|
|
136
|
+
return if File.exist?(db_path)
|
|
137
|
+
|
|
138
|
+
SQLite3::Database.new(db_path).close
|
|
139
|
+
run_schema if schema_exists?
|
|
140
|
+
seed_database
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def seed_database
|
|
144
|
+
return if Seeder.seeded?
|
|
145
|
+
|
|
146
|
+
Seeder.seed_all(verbose: false)
|
|
147
|
+
end
|
|
203
148
|
|
|
204
|
-
# Check if schema file exists.
|
|
205
|
-
#
|
|
206
|
-
# @return [Boolean] true if schema.sql exists
|
|
207
|
-
# @api private
|
|
208
149
|
def schema_exists?
|
|
209
150
|
File.exist?(schema_path)
|
|
210
151
|
end
|
|
211
152
|
|
|
212
|
-
# Run schema SQL file to create database tables.
|
|
213
|
-
#
|
|
214
|
-
# @return [void]
|
|
215
|
-
# @api private
|
|
216
153
|
def run_schema
|
|
217
154
|
connect_to_database
|
|
218
155
|
sql = File.read(schema_path)
|
|
219
|
-
# Use execute_batch to handle multi-statement SQL including triggers
|
|
220
156
|
ActiveRecord::Base.connection.raw_connection.execute_batch(sql)
|
|
221
157
|
end
|
|
222
158
|
end
|
data/src/ruby/tormenta20.rb
CHANGED
|
@@ -33,20 +33,16 @@ module Tormenta20
|
|
|
33
33
|
class << self
|
|
34
34
|
# Configure and setup the database connection.
|
|
35
35
|
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# @param db_path [String, nil] Custom path to SQLite database file
|
|
36
|
+
# Configuration is done via environment variables:
|
|
37
|
+
# - TORMENTA20_DB_MODE: "built_in" (default), "create_on_build", or "path"
|
|
38
|
+
# - TORMENTA20_DB_PATH: Custom path (required when mode is "path")
|
|
39
|
+
#
|
|
41
40
|
# @return [void]
|
|
42
41
|
#
|
|
43
|
-
# @example
|
|
42
|
+
# @example Default usage (built-in database)
|
|
44
43
|
# Tormenta20.setup
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
# Tormenta20.setup(db_path: "/path/to/custom.sqlite3")
|
|
48
|
-
def setup(mode: :builtin, db_path: nil)
|
|
49
|
-
Database.setup(mode: mode, db_path: db_path)
|
|
44
|
+
def setup
|
|
45
|
+
Database.setup
|
|
50
46
|
end
|
|
51
47
|
|
|
52
48
|
# @!group Query Interface
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tormenta20
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- LuanGB
|
|
@@ -55,7 +55,8 @@ description: A Ruby, Node and Python library with data about the Brazilian TTRPG
|
|
|
55
55
|
Tormenta20
|
|
56
56
|
email:
|
|
57
57
|
- luan.goncbs@gmail.com
|
|
58
|
-
executables:
|
|
58
|
+
executables:
|
|
59
|
+
- tormenta20
|
|
59
60
|
extensions: []
|
|
60
61
|
extra_rdoc_files: []
|
|
61
62
|
files:
|
|
@@ -82,6 +83,7 @@ files:
|
|
|
82
83
|
- docs/poderes.md
|
|
83
84
|
- docs/regras.md
|
|
84
85
|
- examples/database_usage.rb
|
|
86
|
+
- exe/tormenta20
|
|
85
87
|
- files.txt
|
|
86
88
|
- package-lock.json
|
|
87
89
|
- package.json
|