tina4ruby 0.5.2 → 3.0.0
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/CHANGELOG.md +1 -1
- data/README.md +360 -559
- data/exe/{tina4 → tina4ruby} +1 -0
- data/lib/tina4/ai.rb +312 -0
- data/lib/tina4/auth.rb +44 -3
- data/lib/tina4/auto_crud.rb +163 -0
- data/lib/tina4/cli.rb +242 -77
- data/lib/tina4/constants.rb +46 -0
- data/lib/tina4/cors.rb +74 -0
- data/lib/tina4/database/sqlite3_adapter.rb +139 -0
- data/lib/tina4/database.rb +43 -7
- data/lib/tina4/debug.rb +4 -79
- data/lib/tina4/dev_admin.rb +1162 -0
- data/lib/tina4/dev_mailbox.rb +191 -0
- data/lib/tina4/dev_reload.rb +9 -9
- data/lib/tina4/drivers/firebird_driver.rb +19 -3
- data/lib/tina4/drivers/mssql_driver.rb +3 -3
- data/lib/tina4/drivers/mysql_driver.rb +4 -4
- data/lib/tina4/drivers/postgres_driver.rb +9 -2
- data/lib/tina4/drivers/sqlite_driver.rb +1 -1
- data/lib/tina4/env.rb +42 -2
- data/lib/tina4/error_overlay.rb +252 -0
- data/lib/tina4/events.rb +90 -0
- data/lib/tina4/field_types.rb +4 -0
- data/lib/tina4/frond.rb +1336 -0
- data/lib/tina4/gallery/auth/meta.json +1 -0
- data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -0
- data/lib/tina4/gallery/database/meta.json +1 -0
- data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -0
- data/lib/tina4/gallery/error-overlay/meta.json +1 -0
- data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -0
- data/lib/tina4/gallery/orm/meta.json +1 -0
- data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -0
- data/lib/tina4/gallery/queue/meta.json +1 -0
- data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +27 -0
- data/lib/tina4/gallery/rest-api/meta.json +1 -0
- data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -0
- data/lib/tina4/gallery/templates/meta.json +1 -0
- data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -0
- data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -0
- data/lib/tina4/health.rb +39 -0
- data/lib/tina4/html_element.rb +148 -0
- data/lib/tina4/localization.rb +2 -2
- data/lib/tina4/log.rb +203 -0
- data/lib/tina4/messenger.rb +484 -0
- data/lib/tina4/migration.rb +132 -29
- data/lib/tina4/orm.rb +337 -31
- data/lib/tina4/public/css/tina4.css +178 -1
- data/lib/tina4/public/css/tina4.min.css +1 -2
- data/lib/tina4/public/favicon.ico +0 -0
- data/lib/tina4/public/images/logo.svg +5 -0
- data/lib/tina4/public/images/tina4-logo-icon.webp +0 -0
- data/lib/tina4/public/js/frond.min.js +420 -0
- data/lib/tina4/public/js/tina4-dev-admin.min.js +367 -0
- data/lib/tina4/public/js/tina4.min.js +93 -0
- data/lib/tina4/public/swagger/index.html +90 -0
- data/lib/tina4/public/swagger/oauth2-redirect.html +63 -0
- data/lib/tina4/queue.rb +40 -4
- data/lib/tina4/queue_backends/lite_backend.rb +88 -0
- data/lib/tina4/rack_app.rb +314 -23
- data/lib/tina4/rate_limiter.rb +123 -0
- data/lib/tina4/request.rb +61 -15
- data/lib/tina4/response.rb +54 -24
- data/lib/tina4/response_cache.rb +134 -0
- data/lib/tina4/router.rb +90 -15
- data/lib/tina4/scss_compiler.rb +2 -2
- data/lib/tina4/seeder.rb +56 -61
- data/lib/tina4/service_runner.rb +303 -0
- data/lib/tina4/session.rb +85 -0
- data/lib/tina4/session_handlers/mongo_handler.rb +1 -1
- data/lib/tina4/session_handlers/valkey_handler.rb +43 -0
- data/lib/tina4/shutdown.rb +84 -0
- data/lib/tina4/sql_translation.rb +295 -0
- data/lib/tina4/template.rb +36 -6
- data/lib/tina4/templates/base.twig +2 -2
- data/lib/tina4/templates/errors/302.twig +14 -0
- data/lib/tina4/templates/errors/401.twig +9 -0
- data/lib/tina4/templates/errors/403.twig +22 -15
- data/lib/tina4/templates/errors/404.twig +22 -15
- data/lib/tina4/templates/errors/500.twig +31 -15
- data/lib/tina4/templates/errors/502.twig +9 -0
- data/lib/tina4/templates/errors/503.twig +12 -0
- data/lib/tina4/templates/errors/base.twig +37 -0
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +28 -18
- data/lib/tina4.rb +57 -21
- metadata +51 -19
- data/lib/tina4/public/js/tina4.js +0 -134
- data/lib/tina4/public/js/tina4helper.js +0 -387
data/lib/tina4/migration.rb
CHANGED
|
@@ -3,41 +3,54 @@ require "fileutils"
|
|
|
3
3
|
|
|
4
4
|
module Tina4
|
|
5
5
|
class Migration
|
|
6
|
-
TRACKING_TABLE = "
|
|
6
|
+
TRACKING_TABLE = "tina4_migration"
|
|
7
|
+
|
|
8
|
+
attr_reader :db, :migrations_dir
|
|
7
9
|
|
|
8
10
|
def initialize(db, migrations_dir: nil)
|
|
9
11
|
@db = db
|
|
10
|
-
@migrations_dir = migrations_dir || File.join(Dir.pwd, "migrations")
|
|
12
|
+
@migrations_dir = migrations_dir || File.join(Dir.pwd, "src", "migrations")
|
|
11
13
|
ensure_tracking_table
|
|
12
14
|
end
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
# Run all pending migrations
|
|
17
|
+
def migrate
|
|
15
18
|
pending = pending_migrations
|
|
16
19
|
if pending.empty?
|
|
17
|
-
Tina4::
|
|
20
|
+
Tina4::Log.info("No pending migrations")
|
|
18
21
|
return []
|
|
19
22
|
end
|
|
20
23
|
|
|
24
|
+
batch = next_batch_number
|
|
21
25
|
results = []
|
|
22
26
|
pending.each do |file|
|
|
23
|
-
result = run_migration(file)
|
|
27
|
+
result = run_migration(file, batch)
|
|
24
28
|
results << result
|
|
29
|
+
# Stop on failure
|
|
30
|
+
break if result[:status] == "failed"
|
|
25
31
|
end
|
|
26
32
|
results
|
|
27
33
|
end
|
|
28
34
|
|
|
35
|
+
alias run migrate
|
|
36
|
+
|
|
37
|
+
# Rollback last batch (or N steps)
|
|
29
38
|
def rollback(steps = 1)
|
|
30
|
-
completed =
|
|
31
|
-
completed.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
completed = completed_migrations_with_batch
|
|
40
|
+
return [] if completed.empty?
|
|
41
|
+
|
|
42
|
+
# Get the last N unique batches
|
|
43
|
+
batches = completed.map { |m| m[:batch] }.uniq.sort.reverse
|
|
44
|
+
batches_to_rollback = batches.first(steps)
|
|
45
|
+
|
|
46
|
+
results = []
|
|
47
|
+
completed.select { |m| batches_to_rollback.include?(m[:batch]) }
|
|
48
|
+
.sort_by { |m| -m[:id] }
|
|
49
|
+
.each do |migration|
|
|
50
|
+
result = rollback_migration(migration[:migration_name])
|
|
51
|
+
results << result
|
|
40
52
|
end
|
|
53
|
+
results
|
|
41
54
|
end
|
|
42
55
|
|
|
43
56
|
def status
|
|
@@ -47,17 +60,30 @@ module Tina4
|
|
|
47
60
|
}
|
|
48
61
|
end
|
|
49
62
|
|
|
63
|
+
# Create a new migration file
|
|
50
64
|
def create(name)
|
|
51
65
|
FileUtils.mkdir_p(@migrations_dir)
|
|
52
66
|
timestamp = Time.now.strftime("%Y%m%d%H%M%S")
|
|
53
|
-
filename = "#{timestamp}_#{name.gsub(/\s+/, '_')}.
|
|
67
|
+
filename = "#{timestamp}_#{name.gsub(/\s+/, '_')}.rb"
|
|
54
68
|
filepath = File.join(@migrations_dir, filename)
|
|
55
|
-
File.write(filepath, "-- Migration: #{name}\n-- Created: #{Time.now}\n\n")
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
File.write(filepath, <<~RUBY)
|
|
71
|
+
# frozen_string_literal: true
|
|
72
|
+
# Migration: #{name}
|
|
73
|
+
# Created: #{Time.now}
|
|
74
|
+
|
|
75
|
+
class #{classify(name)} < Tina4::MigrationBase
|
|
76
|
+
def up(db)
|
|
77
|
+
# db.exec("CREATE TABLE ...")
|
|
78
|
+
end
|
|
59
79
|
|
|
60
|
-
|
|
80
|
+
def down(db)
|
|
81
|
+
# db.exec("DROP TABLE IF EXISTS ...")
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
RUBY
|
|
85
|
+
|
|
86
|
+
Tina4::Log.info("Created migration: #{filename}")
|
|
61
87
|
filepath
|
|
62
88
|
end
|
|
63
89
|
|
|
@@ -69,10 +95,11 @@ module Tina4
|
|
|
69
95
|
CREATE TABLE #{TRACKING_TABLE} (
|
|
70
96
|
id INTEGER PRIMARY KEY,
|
|
71
97
|
migration_name VARCHAR(255) NOT NULL,
|
|
98
|
+
batch INTEGER NOT NULL DEFAULT 1,
|
|
72
99
|
executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
73
100
|
)
|
|
74
101
|
SQL
|
|
75
|
-
Tina4::
|
|
102
|
+
Tina4::Log.info("Created migrations tracking table")
|
|
76
103
|
end
|
|
77
104
|
end
|
|
78
105
|
|
|
@@ -81,29 +108,79 @@ module Tina4
|
|
|
81
108
|
result.map { |r| r[:migration_name] }
|
|
82
109
|
end
|
|
83
110
|
|
|
111
|
+
def completed_migrations_with_batch
|
|
112
|
+
result = @db.fetch("SELECT id, migration_name, batch FROM #{TRACKING_TABLE} ORDER BY id")
|
|
113
|
+
result.map { |r| { id: r[:id], migration_name: r[:migration_name], batch: r[:batch] } }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def next_batch_number
|
|
117
|
+
result = @db.fetch_one("SELECT MAX(batch) as max_batch FROM #{TRACKING_TABLE}")
|
|
118
|
+
(result && result[:max_batch] ? result[:max_batch].to_i : 0) + 1
|
|
119
|
+
end
|
|
120
|
+
|
|
84
121
|
def pending_migrations
|
|
85
122
|
return [] unless Dir.exist?(@migrations_dir)
|
|
86
123
|
|
|
87
124
|
completed = completed_migrations
|
|
88
|
-
|
|
125
|
+
# Support both .rb and .sql migration files
|
|
126
|
+
Dir.glob(File.join(@migrations_dir, "*.{rb,sql}"))
|
|
89
127
|
.reject { |f| f.end_with?(".down.sql") }
|
|
90
128
|
.sort
|
|
91
129
|
.reject { |f| completed.include?(File.basename(f)) }
|
|
92
130
|
end
|
|
93
131
|
|
|
94
|
-
def run_migration(file)
|
|
132
|
+
def run_migration(file, batch)
|
|
95
133
|
name = File.basename(file)
|
|
96
|
-
Tina4::
|
|
134
|
+
Tina4::Log.info("Running migration: #{name}")
|
|
97
135
|
begin
|
|
98
|
-
|
|
99
|
-
|
|
136
|
+
if file.end_with?(".rb")
|
|
137
|
+
execute_ruby_migration(file, :up)
|
|
138
|
+
else
|
|
139
|
+
execute_sql_file(file)
|
|
140
|
+
end
|
|
141
|
+
record_migration(name, batch)
|
|
100
142
|
{ name: name, status: "success" }
|
|
101
143
|
rescue => e
|
|
102
|
-
Tina4::
|
|
144
|
+
Tina4::Log.error("Migration failed: #{name} - #{e.message}")
|
|
103
145
|
{ name: name, status: "failed", error: e.message }
|
|
104
146
|
end
|
|
105
147
|
end
|
|
106
148
|
|
|
149
|
+
def rollback_migration(name)
|
|
150
|
+
Tina4::Log.info("Rolling back: #{name}")
|
|
151
|
+
begin
|
|
152
|
+
file = File.join(@migrations_dir, name)
|
|
153
|
+
if name.end_with?(".rb") && File.exist?(file)
|
|
154
|
+
execute_ruby_migration(file, :down)
|
|
155
|
+
elsif name.end_with?(".sql")
|
|
156
|
+
down_file = File.join(@migrations_dir, name.sub(".sql", ".down.sql"))
|
|
157
|
+
if File.exist?(down_file)
|
|
158
|
+
execute_sql_file(down_file)
|
|
159
|
+
else
|
|
160
|
+
Tina4::Log.warning("No rollback file for: #{name}")
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
remove_migration_record(name)
|
|
164
|
+
{ name: name, status: "rolled_back" }
|
|
165
|
+
rescue => e
|
|
166
|
+
Tina4::Log.error("Rollback failed: #{name} - #{e.message}")
|
|
167
|
+
{ name: name, status: "failed", error: e.message }
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def execute_ruby_migration(file, direction)
|
|
172
|
+
# Load the migration class
|
|
173
|
+
content = File.read(file)
|
|
174
|
+
# Evaluate in a clean binding
|
|
175
|
+
eval(content, TOPLEVEL_BINDING, file)
|
|
176
|
+
|
|
177
|
+
# Find the migration class (last class defined that inherits from MigrationBase)
|
|
178
|
+
class_name = extract_class_name(content)
|
|
179
|
+
klass = Object.const_get(class_name)
|
|
180
|
+
migration = klass.new
|
|
181
|
+
migration.__send__(direction, @db)
|
|
182
|
+
end
|
|
183
|
+
|
|
107
184
|
def execute_sql_file(file)
|
|
108
185
|
sql = File.read(file)
|
|
109
186
|
statements = sql.split(";").map(&:strip).reject(&:empty?)
|
|
@@ -113,12 +190,38 @@ module Tina4
|
|
|
113
190
|
end
|
|
114
191
|
end
|
|
115
192
|
|
|
116
|
-
def record_migration(name)
|
|
117
|
-
@db.insert(TRACKING_TABLE, { migration_name: name })
|
|
193
|
+
def record_migration(name, batch)
|
|
194
|
+
@db.insert(TRACKING_TABLE, { migration_name: name, batch: batch })
|
|
118
195
|
end
|
|
119
196
|
|
|
120
197
|
def remove_migration_record(name)
|
|
121
198
|
@db.delete(TRACKING_TABLE, { migration_name: name })
|
|
122
199
|
end
|
|
200
|
+
|
|
201
|
+
def classify(name)
|
|
202
|
+
name.gsub(/[^a-zA-Z0-9_]/, "_")
|
|
203
|
+
.split("_")
|
|
204
|
+
.map(&:capitalize)
|
|
205
|
+
.join
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def extract_class_name(content)
|
|
209
|
+
if content =~ /class\s+(\w+)\s*<\s*Tina4::MigrationBase/
|
|
210
|
+
$1
|
|
211
|
+
else
|
|
212
|
+
raise "No migration class found inheriting from Tina4::MigrationBase"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Base class for Ruby migrations
|
|
218
|
+
class MigrationBase
|
|
219
|
+
def up(db)
|
|
220
|
+
raise NotImplementedError, "Implement #up in your migration"
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def down(db)
|
|
224
|
+
raise NotImplementedError, "Implement #down in your migration"
|
|
225
|
+
end
|
|
123
226
|
end
|
|
124
227
|
end
|