tina4ruby 3.13.52 → 3.13.53
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/lib/tina4/dev_admin.rb +3 -2
- data/lib/tina4/orm.rb +55 -4
- data/lib/tina4/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 79a09ae5dffe2688af3e75a98dcf999929a1c560f3d8afbdc874590d6dfca275
|
|
4
|
+
data.tar.gz: 92d0d0b44f0e103a3f8ac894c620b075446bab88040d865095e7b9c23ea421fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 128df90a6f281fefb62fc1e49e3075c52474d2c9f70cddc992e1c68a083b8dff38e9c6d014ffec0019736719f49bc2f80cb93eb2f3dff2b3deb336845db1f0e2
|
|
7
|
+
data.tar.gz: c579de40838336727fd25581e8d4db17bd3c449034e4805ba29a8215be60f5344c7608152c6c21cc7258396525e1bc5aee86b12ec4927de62f1745f39797601d
|
data/lib/tina4/dev_admin.rb
CHANGED
|
@@ -1688,8 +1688,9 @@ module Tina4
|
|
|
1688
1688
|
|
|
1689
1689
|
# Native MCP JSON-RPC endpoint (POST /__dev/mcp[/message]). Real MCP
|
|
1690
1690
|
# clients POST a JSON-RPC 2.0 request; we hand the parsed body straight
|
|
1691
|
-
# to the default server's
|
|
1692
|
-
# notification (no id) yields an empty
|
|
1691
|
+
# to the default server's dispatch_http and echo the response. A
|
|
1692
|
+
# notification (no id) yields an empty 202 Accepted (the MCP Streamable
|
|
1693
|
+
# HTTP contract), mirroring the Python master, PHP and Node.
|
|
1693
1694
|
# Streamable HTTP POST /__dev/mcp — the current MCP transport. initialize
|
|
1694
1695
|
# issues an Mcp-Session-Id; an unknown session on a non-initialize request
|
|
1695
1696
|
# is a 404; a notification is 202; anything else answers inline (200).
|
data/lib/tina4/orm.rb
CHANGED
|
@@ -419,6 +419,20 @@ module Tina4
|
|
|
419
419
|
else "DATETIME"
|
|
420
420
|
end
|
|
421
421
|
|
|
422
|
+
# Engine-aware JSON column type (parity with the Python master's
|
|
423
|
+
# JSONField DDL). PostgreSQL gets native JSONB (indexable, canonical);
|
|
424
|
+
# MySQL native JSON; MSSQL stores JSON as NVARCHAR(MAX) (its JSON
|
|
425
|
+
# functions read that); Firebird has no TEXT type so it uses a text
|
|
426
|
+
# BLOB; SQLite and everything else store the JSON text in TEXT
|
|
427
|
+
# (queryable via json1).
|
|
428
|
+
json_sql = case engine
|
|
429
|
+
when "postgres", "postgresql" then "JSONB"
|
|
430
|
+
when "mysql" then "JSON"
|
|
431
|
+
when "mssql", "sqlserver" then "NVARCHAR(MAX)"
|
|
432
|
+
when "firebird" then "BLOB SUB_TYPE TEXT"
|
|
433
|
+
else "TEXT"
|
|
434
|
+
end
|
|
435
|
+
|
|
422
436
|
type_map = {
|
|
423
437
|
integer: "INTEGER",
|
|
424
438
|
string: "VARCHAR(255)",
|
|
@@ -430,7 +444,7 @@ module Tina4
|
|
|
430
444
|
datetime: datetime_sql,
|
|
431
445
|
timestamp: "TIMESTAMP",
|
|
432
446
|
blob: "BLOB",
|
|
433
|
-
json:
|
|
447
|
+
json: json_sql
|
|
434
448
|
}
|
|
435
449
|
|
|
436
450
|
col_defs = []
|
|
@@ -444,7 +458,11 @@ module Tina4
|
|
|
444
458
|
parts << "PRIMARY KEY" if opts[:primary_key]
|
|
445
459
|
parts << "AUTOINCREMENT" if opts[:auto_increment]
|
|
446
460
|
parts << "NOT NULL" if !opts[:nullable] && !opts[:primary_key]
|
|
447
|
-
|
|
461
|
+
# A JSON column carries no DDL DEFAULT (parity with the Python master):
|
|
462
|
+
# a dict/list default is an application-level concern, applied per
|
|
463
|
+
# instance, not a portable SQL literal (PG needs a ::jsonb cast, MySQL
|
|
464
|
+
# an expression default). The instance still gets its default at new.
|
|
465
|
+
if opts[:default] && !opts[:auto_increment] && opts[:type] != :json
|
|
448
466
|
parts << "DEFAULT #{default_literal(opts[:default], opts[:type], bool_sql)}"
|
|
449
467
|
end
|
|
450
468
|
col_defs << parts.join(" ")
|
|
@@ -486,6 +504,21 @@ module Tina4
|
|
|
486
504
|
hash.each do |key, value|
|
|
487
505
|
# Apply field mapping (db_col => ruby_attr)
|
|
488
506
|
attr_name = mapping_reverse[key.to_s] || key
|
|
507
|
+
# A JSON column comes back from the driver as a JSON string (SQLite
|
|
508
|
+
# TEXT, MySQL JSON, PostgreSQL JSONB via the text protocol, MSSQL
|
|
509
|
+
# NVARCHAR). Decode it to the Hash/Array the attribute expects
|
|
510
|
+
# (parity with the Python master's JSONField parse-on-read). A value
|
|
511
|
+
# already a Hash/Array is left untouched; nil stays nil; a
|
|
512
|
+
# non-decodable string keeps its raw form rather than crashing a
|
|
513
|
+
# normal read.
|
|
514
|
+
fdef = field_definitions[attr_name.to_sym]
|
|
515
|
+
if fdef && fdef[:type] == :json && value.is_a?(String)
|
|
516
|
+
begin
|
|
517
|
+
value = JSON.parse(value)
|
|
518
|
+
rescue JSON::ParserError
|
|
519
|
+
# leave the raw string in place
|
|
520
|
+
end
|
|
521
|
+
end
|
|
489
522
|
setter = "#{attr_name}="
|
|
490
523
|
instance.__send__(setter, value) if instance.respond_to?(setter)
|
|
491
524
|
end
|
|
@@ -610,6 +643,11 @@ module Tina4
|
|
|
610
643
|
if __send__(name).nil? && opts[:default]
|
|
611
644
|
d = opts[:default]
|
|
612
645
|
d = d.call if d.respond_to?(:call) && !d.is_a?(Class)
|
|
646
|
+
# Deep-copy a mutable Hash/Array default so two instances never alias
|
|
647
|
+
# the same object (e.g. `json_field :meta, default: {}` — mutating
|
|
648
|
+
# a.meta must not leak into b.meta). Parity with the Python master,
|
|
649
|
+
# which deepcopies a JSONField's dict/list default per instance.
|
|
650
|
+
d = Marshal.load(Marshal.dump(d)) if d.is_a?(Hash) || d.is_a?(Array)
|
|
613
651
|
__send__("#{name}=", d)
|
|
614
652
|
end
|
|
615
653
|
end
|
|
@@ -667,7 +705,6 @@ module Tina4
|
|
|
667
705
|
return false
|
|
668
706
|
end
|
|
669
707
|
|
|
670
|
-
data = to_db_hash(exclude_nil: true)
|
|
671
708
|
pk = self.class.primary_key_field || :id
|
|
672
709
|
pk_value = __send__(pk)
|
|
673
710
|
pk_opts = self.class.field_definitions[pk] || {}
|
|
@@ -694,6 +731,10 @@ module Tina4
|
|
|
694
731
|
end
|
|
695
732
|
|
|
696
733
|
begin
|
|
734
|
+
# Built here (inside the rescue's reach) so a JSON column that can't be
|
|
735
|
+
# serialised (to_db_hash raises JSON::GeneratorError) fails loud through
|
|
736
|
+
# the same path as a driver error — rolled back, false, cause recorded.
|
|
737
|
+
data = to_db_hash(exclude_nil: true)
|
|
697
738
|
self.class.db.transaction do |db|
|
|
698
739
|
if is_update
|
|
699
740
|
filter = { pk => pk_value }
|
|
@@ -941,9 +982,19 @@ module Tina4
|
|
|
941
982
|
def to_db_hash(exclude_nil: false)
|
|
942
983
|
hash = {}
|
|
943
984
|
mapping = self.class.field_mapping
|
|
944
|
-
self.class.field_definitions.
|
|
985
|
+
self.class.field_definitions.each do |name, opts|
|
|
945
986
|
value = __send__(name)
|
|
946
987
|
next if exclude_nil && value.nil?
|
|
988
|
+
# A JSON column serialises its Hash/Array to a JSON string for the
|
|
989
|
+
# driver (parity with the Python master's JSONField.to_db). A value
|
|
990
|
+
# that can't be encoded (e.g. Float::NAN) raises JSON::GeneratorError;
|
|
991
|
+
# save() computes this hash inside its rescue, so it fails loud —
|
|
992
|
+
# rolls back, returns false, records the cause. A value already a
|
|
993
|
+
# String is left as-is (a caller who pre-encoded stays in control);
|
|
994
|
+
# nil passes through untouched.
|
|
995
|
+
if opts[:type] == :json && !value.nil? && !value.is_a?(String)
|
|
996
|
+
value = JSON.generate(value)
|
|
997
|
+
end
|
|
947
998
|
db_col = mapping[name.to_s] || name
|
|
948
999
|
hash[db_col.to_sym] = value
|
|
949
1000
|
end
|
data/lib/tina4/version.rb
CHANGED
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.53
|
|
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-07-
|
|
11
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|