tina4ruby 3.13.139 → 3.13.141
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 +26 -0
- data/lib/tina4/database/sqlite3_adapter.rb +6 -54
- data/lib/tina4/drivers/sqlite_driver.rb +6 -81
- data/lib/tina4/row_symbolizer.rb +54 -0
- data/lib/tina4/sqlite_path_resolver.rb +72 -0
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4.rb +2 -0
- 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: 0aa7d16e6192a9b815d104ed73e28d9dcfc0059017c8980cd2fa6593f23f3c95
|
|
4
|
+
data.tar.gz: c9eb3c6c4141f58c7539fc65ad1d7596dca9602968b1094d6c6a3b236492aa6b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 11b4e161b6412ca687ff1022e1575336f5aeb6b17c54ee72d05db86d045cba8b5e4e5aa52c126fe7f761a234fcfec5215b8cd8a4ae5284c5738f84a4dea91b13
|
|
7
|
+
data.tar.gz: b80d9c080a764c3ecd5912f6a7081e61f5196e1f751a86a52da62612f46223bea3148c203dbc549846ba916aadda20c831a1310472179dcd3f888da016624323
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,32 @@ number means the same thing everywhere.
|
|
|
6
6
|
**The authoritative release notes for every shipped version live in the documentation:**
|
|
7
7
|
https://tina4.com/ruby/36-releases
|
|
8
8
|
|
|
9
|
+
## 3.13.141 — 2026-09-29
|
|
10
|
+
|
|
11
|
+
A hardening release. The sibling php framework shipped committed absolute symlinks that broke `composer install` on Windows, so every framework now refuses a committed symlink up front. Ruby carries no such link today, so this guard is preventive parity: it keeps the door shut before anything can wander in. The framework still carries no required runtime dependencies.
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
- A new `scripts/check-no-symlinks.sh` gate refuses any committed symlink (git mode 120000). A committed symlink breaks Windows and Composer extraction and is a supply-chain risk. The gate runs first in the test workflow, and a real RSpec spec proves it by mutation — it stages a symlink in a throwaway git repo, watches the guard go red, then removes it and watches it go green.
|
|
15
|
+
|
|
16
|
+
## 3.13.140 — 2026-09-28
|
|
17
|
+
|
|
18
|
+
A housekeeping release that tightens the guards and dedups the SQLite plumbing. The CLAUDE.md doc-drift gate now watches this repo, so a doc that names a Tina4 method the code no longer has fails loudly instead of drifting on quietly. The SQLite path resolver learnt some manners: an escaping relative path is refused, and it creates the parent folders itself at 0775 (ADR-0086). The framework still carries no required runtime dependencies.
|
|
19
|
+
|
|
20
|
+
### Fixes
|
|
21
|
+
- SQLite refuses a relative path that escapes the project and creates its parent directories at 0775, hardening ADR-0086.
|
|
22
|
+
- The maintainer skills stale check resolves correctly and warns when an addGlobal closure would go stale (skill bugs 90/91).
|
|
23
|
+
|
|
24
|
+
### Refactors
|
|
25
|
+
- The SQLite `resolve_path` logic moves into one shared `SqlitePathResolver` module, so every caller reads the same path the same way.
|
|
26
|
+
- `symbolize_rows` and `symbolize_keys` collapse into one shared module, characterised first so the behaviour cannot shift.
|
|
27
|
+
|
|
28
|
+
### Docs
|
|
29
|
+
- The Web Push skill mirrors sync back to the `.claude` canonical, so the two copies agree.
|
|
30
|
+
|
|
31
|
+
### CI
|
|
32
|
+
- A CLAUDE.md doc-drift gate lands, the tina4-js rtc.md mirror syncs, and the SQLTranslator doc drift is fixed.
|
|
33
|
+
- The CLI crud generator locks in single pluralisation, so an `Order` model becomes `orders`, never `orderss`.
|
|
34
|
+
|
|
9
35
|
## 3.13.139
|
|
10
36
|
|
|
11
37
|
Outbound requests from the Api client and Web Push are guarded against SSRF: private, loopback and link-local targets are refused unless explicitly allowed (ADR-0084). The development admin surface is hardened -- the health endpoint no longer discloses the framework version outside debug, and development binds loopback by default (ADR-0078). Production refuses to boot with a blank or short HMAC secret so tokens cannot be forged (ADR-0079). Queue reject() dead-letters immediately and size("dead") reports the dead-letter depth (ADR-0022, ADR-0023). Session table first-use is concurrency-safe across every engine. Mail encryption follows the configured TLS/STARTTLS contract (ADR-0071). Log output, driver-install hints and WSDL/mail diagnostics redact credentials and URLs. Publishing is gated behind a protected environment with release actions pinned by commit SHA, and a medium-severity API token-leak path is closed. The framework still has no required runtime dependencies.
|
|
@@ -7,10 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
require_relative "../sqlite3_gem"
|
|
10
|
+
require_relative "../sqlite_path_resolver"
|
|
11
|
+
require_relative "../row_symbolizer"
|
|
10
12
|
|
|
11
13
|
module Tina4
|
|
12
14
|
module Adapters
|
|
13
15
|
class Sqlite3Adapter
|
|
16
|
+
# Shared SQLite path resolution — one home for both SQLite resolvers.
|
|
17
|
+
extend Tina4::SqlitePathResolver
|
|
18
|
+
include Tina4::RowSymbolizer
|
|
19
|
+
|
|
14
20
|
attr_reader :connection, :db_path
|
|
15
21
|
|
|
16
22
|
def initialize(connection_string = nil)
|
|
@@ -30,32 +36,6 @@ module Tina4
|
|
|
30
36
|
self
|
|
31
37
|
end
|
|
32
38
|
|
|
33
|
-
# Resolve a SQLite URL / path against the project root (cwd).
|
|
34
|
-
# Convention matches Tina4::Drivers::SqliteDriver.resolve_path —
|
|
35
|
-
# three slashes = relative, four = absolute, drive letter = Windows abs.
|
|
36
|
-
def self.resolve_path(connection_string)
|
|
37
|
-
return ":memory:" if connection_string == "sqlite::memory:" || connection_string == "sqlite:///:memory:"
|
|
38
|
-
|
|
39
|
-
raw = connection_string.to_s
|
|
40
|
-
.sub(/^sqlite3?:\/\/\//, "")
|
|
41
|
-
.sub(/^sqlite3?:\/\//, "")
|
|
42
|
-
.sub(/^sqlite3?:/, "")
|
|
43
|
-
return ":memory:" if raw == ":memory:"
|
|
44
|
-
|
|
45
|
-
is_windows_abs = raw.match?(/^[A-Za-z]:[\/\\]/)
|
|
46
|
-
is_unix_abs = raw.start_with?("/")
|
|
47
|
-
|
|
48
|
-
if is_windows_abs || is_unix_abs
|
|
49
|
-
raw
|
|
50
|
-
else
|
|
51
|
-
resolved = File.join(Dir.pwd, raw)
|
|
52
|
-
parent = File.dirname(resolved)
|
|
53
|
-
require "fileutils"
|
|
54
|
-
FileUtils.mkdir_p(parent) unless File.directory?(parent)
|
|
55
|
-
resolved
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
39
|
def close
|
|
60
40
|
@connection.close if @connection
|
|
61
41
|
@connection = nil
|
|
@@ -163,34 +143,6 @@ module Tina4
|
|
|
163
143
|
def apply_limit(sql, limit, offset = 0)
|
|
164
144
|
"#{sql}\nLIMIT #{limit} OFFSET #{offset}"
|
|
165
145
|
end
|
|
166
|
-
|
|
167
|
-
private
|
|
168
|
-
|
|
169
|
-
# Symbolize a whole result set's keys, computing the mapping ONCE per query
|
|
170
|
-
# rather than per cell. See the matching helper in drivers/sqlite_driver.rb
|
|
171
|
-
# for the rationale: the per-row form ran k.to_s.to_sym for every cell, so a
|
|
172
|
-
# 5,000-row x 6-column fetch did 30,000 conversions for 6 distinct keys. The
|
|
173
|
-
# is_a? guard still drops any positional Integer keys older gems emit.
|
|
174
|
-
def symbolize_rows(rows)
|
|
175
|
-
return rows if rows.empty?
|
|
176
|
-
|
|
177
|
-
str_keys = rows.first.keys.select { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
|
178
|
-
sym_keys = str_keys.map(&:to_sym)
|
|
179
|
-
count = str_keys.length
|
|
180
|
-
rows.map do |row|
|
|
181
|
-
out = {}
|
|
182
|
-
i = 0
|
|
183
|
-
while i < count
|
|
184
|
-
out[sym_keys[i]] = row[str_keys[i]]
|
|
185
|
-
i += 1
|
|
186
|
-
end
|
|
187
|
-
out
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
def symbolize_keys(hash)
|
|
192
|
-
symbolize_rows([hash]).first
|
|
193
|
-
end
|
|
194
146
|
end
|
|
195
147
|
end
|
|
196
148
|
end
|
|
@@ -8,11 +8,17 @@
|
|
|
8
8
|
|
|
9
9
|
require_relative "schema_split"
|
|
10
10
|
require_relative "../sqlite3_gem"
|
|
11
|
+
require_relative "../sqlite_path_resolver"
|
|
12
|
+
require_relative "../row_symbolizer"
|
|
11
13
|
|
|
12
14
|
module Tina4
|
|
13
15
|
module Drivers
|
|
14
16
|
class SqliteDriver
|
|
17
|
+
# Shared SQLite path resolution — one home for both SQLite resolvers.
|
|
18
|
+
extend Tina4::SqlitePathResolver
|
|
19
|
+
|
|
15
20
|
include Tina4::DatabaseAdapter
|
|
21
|
+
include Tina4::RowSymbolizer
|
|
16
22
|
include SchemaSplit
|
|
17
23
|
attr_reader :connection
|
|
18
24
|
|
|
@@ -51,50 +57,6 @@ module Tina4
|
|
|
51
57
|
@connection.execute("PRAGMA foreign_keys=ON")
|
|
52
58
|
end
|
|
53
59
|
|
|
54
|
-
# Resolve a SQLite URL / path against the project root (cwd).
|
|
55
|
-
#
|
|
56
|
-
# Convention (matches tina4-python, tina4-php, tina4-nodejs):
|
|
57
|
-
# sqlite::memory: → :memory:
|
|
58
|
-
# sqlite:///:memory: → :memory:
|
|
59
|
-
# sqlite:///app.db → {cwd}/app.db (relative)
|
|
60
|
-
# sqlite:///data/app.db → {cwd}/data/app.db (relative; auto-mkdir under cwd)
|
|
61
|
-
# sqlite:////var/data/app.db → /var/data/app.db (absolute; no auto-mkdir)
|
|
62
|
-
# sqlite:///C:/Users/app.db → C:/Users/app.db (Windows absolute)
|
|
63
|
-
#
|
|
64
|
-
# Never mkdir outside cwd — that was the root cause of the
|
|
65
|
-
# "Read-only file system: '/data'" crash on macOS.
|
|
66
|
-
def self.resolve_path(connection_string)
|
|
67
|
-
return ":memory:" if connection_string == "sqlite::memory:" || connection_string == "sqlite:///:memory:"
|
|
68
|
-
|
|
69
|
-
# Strip the scheme + up to three slashes, preserving a potential fourth
|
|
70
|
-
# slash (absolute) or drive letter.
|
|
71
|
-
# `sqlite3:` is a documented alias for `sqlite:`. Normalise it FIRST or
|
|
72
|
-
# none of the strips below match and `raw` keeps the whole connection
|
|
73
|
-
# string, so the database file is literally named "sqlite3:app.db".
|
|
74
|
-
# Not merely ugly: a colon is an illegal filename character on Windows,
|
|
75
|
-
# so the documented alias is unusable there. DatabaseUrl already
|
|
76
|
-
# normalises it; this method duplicates the strip instead of calling
|
|
77
|
-
# it, which is how the two drifted.
|
|
78
|
-
normalised = connection_string.sub(/^sqlite3:/, "sqlite:")
|
|
79
|
-
raw = normalised.sub(/^sqlite:\/\/\//, "").sub(/^sqlite:\/\//, "").sub(/^sqlite:/, "")
|
|
80
|
-
return ":memory:" if raw == ":memory:"
|
|
81
|
-
|
|
82
|
-
is_windows_abs = raw.match?(/^[A-Za-z]:[\/\\]/)
|
|
83
|
-
is_unix_abs = raw.start_with?("/")
|
|
84
|
-
|
|
85
|
-
if is_windows_abs || is_unix_abs
|
|
86
|
-
# Absolute — trust the user; don't auto-mkdir outside cwd.
|
|
87
|
-
raw
|
|
88
|
-
else
|
|
89
|
-
# Relative — resolve under cwd; auto-mkdir parent dir.
|
|
90
|
-
resolved = File.join(Dir.pwd, raw)
|
|
91
|
-
parent = File.dirname(resolved)
|
|
92
|
-
require "fileutils"
|
|
93
|
-
FileUtils.mkdir_p(parent) unless File.directory?(parent)
|
|
94
|
-
resolved
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
60
|
def close
|
|
99
61
|
@connection&.close
|
|
100
62
|
end
|
|
@@ -227,43 +189,6 @@ module Tina4
|
|
|
227
189
|
def identifier?(str)
|
|
228
190
|
str.to_s.match?(/\A[A-Za-z_][A-Za-z0-9_]*\z/)
|
|
229
191
|
end
|
|
230
|
-
|
|
231
|
-
# Symbolize the keys of a whole result set, computing the key mapping ONCE.
|
|
232
|
-
#
|
|
233
|
-
# results_as_hash=true hands back string-keyed hashes; every row of a query
|
|
234
|
-
# has the same columns, so the string->symbol conversion is identical for
|
|
235
|
-
# every row. The old code called `symbolize_keys` per row, which ran
|
|
236
|
-
# `k.to_s.to_sym` and an `is_a?` guard PER CELL: a 5,000-row x 6-column
|
|
237
|
-
# fetch did 30,000 conversions for 6 distinct keys and made Tina4 the
|
|
238
|
-
# slowest of the four frameworks at bulk reads (Select ALL 5,000 rows:
|
|
239
|
-
# 10.16ms vs raw sqlite3 2.56ms). Hoisting the mapping out of the loop
|
|
240
|
-
# roughly halves the hydration cost with byte-identical output.
|
|
241
|
-
#
|
|
242
|
-
# The is_a?(String|Symbol) guard is preserved: older sqlite3 gems put
|
|
243
|
-
# positional Integer keys in the hash alongside the string names, and those
|
|
244
|
-
# must still be dropped.
|
|
245
|
-
def symbolize_rows(rows)
|
|
246
|
-
return rows if rows.empty?
|
|
247
|
-
|
|
248
|
-
str_keys = rows.first.keys.select { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
|
249
|
-
sym_keys = str_keys.map(&:to_sym)
|
|
250
|
-
count = str_keys.length
|
|
251
|
-
rows.map do |row|
|
|
252
|
-
out = {}
|
|
253
|
-
i = 0
|
|
254
|
-
while i < count
|
|
255
|
-
out[sym_keys[i]] = row[str_keys[i]]
|
|
256
|
-
i += 1
|
|
257
|
-
end
|
|
258
|
-
out
|
|
259
|
-
end
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
# Single-hash convenience kept for any external caller; delegates to the
|
|
263
|
-
# batch path so there is one implementation of the mapping rule.
|
|
264
|
-
def symbolize_keys(hash)
|
|
265
|
-
symbolize_rows([hash]).first
|
|
266
|
-
end
|
|
267
192
|
end
|
|
268
193
|
end
|
|
269
194
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright (c) 2026 Code Infinity
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
|
|
8
|
+
module Tina4
|
|
9
|
+
# Shared row/key symbolisation for the two SQLite classes.
|
|
10
|
+
#
|
|
11
|
+
# `symbolize_rows`/`symbolize_keys` used to be byte-identical copy-paste in
|
|
12
|
+
# Tina4::Drivers::SqliteDriver and Tina4::Adapters::Sqlite3Adapter (the
|
|
13
|
+
# adapter's own comment pointed at the driver's copy). This module is the
|
|
14
|
+
# single home for the logic; both classes `include` it as private instance
|
|
15
|
+
# methods, so both share ONE implementation and can never drift again.
|
|
16
|
+
#
|
|
17
|
+
# The mapping is computed ONCE per query rather than per cell: the per-row
|
|
18
|
+
# form ran `k.to_s.to_sym` and an `is_a?` guard PER CELL, so a 5,000-row x
|
|
19
|
+
# 6-column fetch did 30,000 conversions for 6 distinct keys and made Tina4 the
|
|
20
|
+
# slowest of the four frameworks at bulk reads (Select ALL 5,000 rows:
|
|
21
|
+
# 10.16ms vs raw sqlite3 2.56ms). Hoisting the mapping out of the loop roughly
|
|
22
|
+
# halves the hydration cost with byte-identical output.
|
|
23
|
+
#
|
|
24
|
+
# The is_a?(String|Symbol) guard is preserved: older sqlite3 gems put
|
|
25
|
+
# positional Integer keys in the hash alongside the string names, and those
|
|
26
|
+
# must still be dropped.
|
|
27
|
+
module RowSymbolizer
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
# Symbolize a whole result set's keys, computing the mapping once per query.
|
|
31
|
+
def symbolize_rows(rows)
|
|
32
|
+
return rows if rows.empty?
|
|
33
|
+
|
|
34
|
+
str_keys = rows.first.keys.select { |k| k.is_a?(String) || k.is_a?(Symbol) }
|
|
35
|
+
sym_keys = str_keys.map(&:to_sym)
|
|
36
|
+
count = str_keys.length
|
|
37
|
+
rows.map do |row|
|
|
38
|
+
out = {}
|
|
39
|
+
i = 0
|
|
40
|
+
while i < count
|
|
41
|
+
out[sym_keys[i]] = row[str_keys[i]]
|
|
42
|
+
i += 1
|
|
43
|
+
end
|
|
44
|
+
out
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Single-hash convenience; delegates to the batch path so there is one
|
|
49
|
+
# implementation of the mapping rule.
|
|
50
|
+
def symbolize_keys(hash)
|
|
51
|
+
symbolize_rows([hash]).first
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copyright (c) 2026 Code Infinity
|
|
3
|
+
# SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
5
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
6
|
+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
7
|
+
|
|
8
|
+
require "fileutils"
|
|
9
|
+
|
|
10
|
+
module Tina4
|
|
11
|
+
# Shared SQLite database-path resolution for the two SQLite resolvers.
|
|
12
|
+
#
|
|
13
|
+
# `resolve_path` used to be copy-pasted into Tina4::Drivers::SqliteDriver and
|
|
14
|
+
# Tina4::Adapters::Sqlite3Adapter with drifted scheme-stripping. This module is
|
|
15
|
+
# the single home for the logic; both classes `extend` it, so both keep a
|
|
16
|
+
# `.resolve_path` class method backed by ONE implementation and can never
|
|
17
|
+
# drift again.
|
|
18
|
+
#
|
|
19
|
+
# Convention (matches tina4-python, tina4-php, tina4-nodejs):
|
|
20
|
+
# sqlite::memory: → :memory:
|
|
21
|
+
# sqlite:///:memory: → :memory:
|
|
22
|
+
# sqlite:///app.db → {cwd}/app.db (relative)
|
|
23
|
+
# sqlite:///data/app.db → {cwd}/data/app.db (relative; auto-mkdir under cwd)
|
|
24
|
+
# sqlite:////var/data/app.db → /var/data/app.db (absolute; no auto-mkdir)
|
|
25
|
+
# sqlite:///C:/Users/app.db → C:/Users/app.db (Windows absolute)
|
|
26
|
+
#
|
|
27
|
+
# `sqlite3:` is a documented alias for `sqlite:` and is normalised first.
|
|
28
|
+
# Never mkdir outside cwd — that was the root cause of the
|
|
29
|
+
# "Read-only file system: '/data'" crash on macOS.
|
|
30
|
+
module SqlitePathResolver
|
|
31
|
+
# Resolve a SQLite URL / path against the project root (cwd).
|
|
32
|
+
#
|
|
33
|
+
# @param connection_string [String] a sqlite: / sqlite3: URL or bare path
|
|
34
|
+
# @return [String] the resolved path to hand to the driver, or ":memory:"
|
|
35
|
+
def resolve_path(connection_string)
|
|
36
|
+
return ":memory:" if connection_string == "sqlite::memory:" || connection_string == "sqlite:///:memory:"
|
|
37
|
+
|
|
38
|
+
# `sqlite3:` is a documented alias for `sqlite:`; normalise it FIRST or the
|
|
39
|
+
# strips below miss and the file ends up literally named "sqlite3:app.db"
|
|
40
|
+
# (and a colon is an illegal filename character on Windows).
|
|
41
|
+
normalised = connection_string.to_s.sub(/^sqlite3:/, "sqlite:")
|
|
42
|
+
raw = normalised.sub(%r{^sqlite:///}, "").sub(%r{^sqlite://}, "").sub(/^sqlite:/, "")
|
|
43
|
+
return ":memory:" if raw == ":memory:"
|
|
44
|
+
|
|
45
|
+
is_windows_abs = raw.match?(%r{^[A-Za-z]:[/\\]})
|
|
46
|
+
is_unix_abs = raw.start_with?("/")
|
|
47
|
+
|
|
48
|
+
if is_windows_abs || is_unix_abs
|
|
49
|
+
# Absolute — trust the user; don't auto-mkdir outside cwd.
|
|
50
|
+
raw
|
|
51
|
+
else
|
|
52
|
+
# Relative — resolve under cwd; create the parent (mode 0775) ONLY when
|
|
53
|
+
# it stays inside cwd (ADR-0086). A relative path whose parent escapes
|
|
54
|
+
# the project (e.g. "../../etc/foo.db") is REFUSED loudly here — never
|
|
55
|
+
# the silent mkdir outside the project that used to happen.
|
|
56
|
+
resolved = File.join(Dir.pwd, raw)
|
|
57
|
+
parent = File.dirname(resolved)
|
|
58
|
+
expanded_parent = File.expand_path(parent)
|
|
59
|
+
cwd = File.expand_path(Dir.pwd)
|
|
60
|
+
unless expanded_parent == cwd || expanded_parent.start_with?(cwd + File::SEPARATOR)
|
|
61
|
+
raise ArgumentError,
|
|
62
|
+
"SQLite path #{connection_string.inspect} resolves outside the project " \
|
|
63
|
+
"directory: #{expanded_parent.inspect} is not within #{cwd.inspect}. " \
|
|
64
|
+
"Tina4 refuses to create directories outside the project (ADR-0086). " \
|
|
65
|
+
"Use an absolute path for a database that lives outside the project."
|
|
66
|
+
end
|
|
67
|
+
FileUtils.mkdir_p(parent, mode: 0o775) unless File.directory?(parent)
|
|
68
|
+
resolved
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
data/lib/tina4/version.rb
CHANGED
data/lib/tina4.rb
CHANGED
|
@@ -12,6 +12,8 @@ require_relative "tina4/constants"
|
|
|
12
12
|
require_relative "tina4/log"
|
|
13
13
|
require_relative "tina4/debug" # backward compat alias
|
|
14
14
|
require_relative "tina4/env"
|
|
15
|
+
require_relative "tina4/sqlite_path_resolver"
|
|
16
|
+
require_relative "tina4/row_symbolizer"
|
|
15
17
|
require_relative "tina4/router"
|
|
16
18
|
require_relative "tina4/request"
|
|
17
19
|
require_relative "tina4/response"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tina4ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.13.
|
|
4
|
+
version: 3.13.141
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tina4 Team
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sqlite3
|
|
@@ -327,6 +327,7 @@ files:
|
|
|
327
327
|
- lib/tina4/response.rb
|
|
328
328
|
- lib/tina4/response_cache.rb
|
|
329
329
|
- lib/tina4/router.rb
|
|
330
|
+
- lib/tina4/row_symbolizer.rb
|
|
330
331
|
- lib/tina4/secret_file.rb
|
|
331
332
|
- lib/tina4/seeder.rb
|
|
332
333
|
- lib/tina4/service.rb
|
|
@@ -344,6 +345,7 @@ files:
|
|
|
344
345
|
- lib/tina4/smtp_client.rb
|
|
345
346
|
- lib/tina4/sql_translator.rb
|
|
346
347
|
- lib/tina4/sqlite3_gem.rb
|
|
348
|
+
- lib/tina4/sqlite_path_resolver.rb
|
|
347
349
|
- lib/tina4/sso.rb
|
|
348
350
|
- lib/tina4/ssrf.rb
|
|
349
351
|
- lib/tina4/swagger.rb
|