tina4ruby 3.13.9 → 3.13.12

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c247ddb7851f9f5e0f8cfb8a7a74a4e7690cd9fe606d5d32f7603aa9547aff1d
4
- data.tar.gz: 75d979c50632935abe4c4864d3c0f28065b3ad87e2f13a81db999f04379fcfb8
3
+ metadata.gz: 303058fcf9f267e5034b659e75be74b7a5fba69740968063b7370ff046452c8c
4
+ data.tar.gz: 993d93df6a783588a4b6c33bd8737a522c5dba96aafefb9dcc701bc6a30eb6c0
5
5
  SHA512:
6
- metadata.gz: ed1956eb82a7ce8242fcbb6c95b14613d72a6931c62bda8bf2e6944f7f77f61e838f40f0a2aeac031f8392d4230407129e2d0edd958c2ab4db2ecc5831fb0570
7
- data.tar.gz: a8fed6c6985d6f717c083c977ef26df48ddf55af67816e4595918ef4183a5d78c3cea7c98e0849a8b1407cf5ef34f67b69840de0465d4b086561c2fa2ab3e1ac
6
+ metadata.gz: a0c1c3f7a454e6cee7b4512290b8b0cf3de3c1683d1f797f64cc7efc8caa5e1cd6a245a0d96351f151b878b49e57e8a2461c6f120151b0f1bcc660d0f1dcdd0c
7
+ data.tar.gz: 0d4eb1114071ca5173b36b73ce1af9c705e23a69d8aff1b45b335b1ba5587ca9fee4825b6dba1f9cacdab5cde01e2c64e77d73bd70950a7f976f64f3e779c583
@@ -82,6 +82,21 @@ module Tina4
82
82
  "odbc" => "Tina4::Drivers::OdbcDriver"
83
83
  }.freeze
84
84
 
85
+ # v3.13.12 — strip trailing `;` from user SQL before the framework
86
+ # wraps it with COUNT(*) subqueries or appends LIMIT/OFFSET. Without
87
+ # this, ``"SELECT * FROM t;"`` produces ``"SELECT * FROM t; LIMIT 100
88
+ # OFFSET 0"`` — a syntax error on every engine. Internal semicolons
89
+ # (inside string literals, between meaningful statements) are left
90
+ # alone; drivers reject those if multi-statement isn't supported.
91
+ def self.strip_trailing_semicolons(sql)
92
+ return sql if sql.nil? || sql.empty?
93
+ stripped = sql.rstrip
94
+ while stripped.end_with?(";")
95
+ stripped = stripped[0..-2].rstrip
96
+ end
97
+ stripped
98
+ end
99
+
85
100
  # Static factory — cross-framework consistency: Database.create(url)
86
101
  def self.create(url, username: "", password: "", pool: nil)
87
102
  new(url, username: username.empty? ? nil : username,
@@ -245,7 +260,11 @@ module Tina4
245
260
  # rows.each { |row| puts row["name"] }
246
261
  #
247
262
  # Returns [] (not nil) when no rows match.
248
- def fetch_all(sql, params = [], limit: 100, offset: nil)
263
+ #
264
+ # v3.13.12: default `limit` is **nil** (no truncation) — the method
265
+ # name says fetch_all, so it returns all matching rows. Pre-v3.13.12
266
+ # silently truncated to 100. Pass an explicit `limit:` to cap.
267
+ def fetch_all(sql, params = [], limit: nil, offset: nil)
249
268
  fetch(sql, params, limit: limit, offset: offset).records
250
269
  end
251
270
 
@@ -253,6 +272,13 @@ module Tina4
253
272
  offset ||= 0
254
273
  drv = current_driver
255
274
 
275
+ # v3.13.12: strip trailing `;` so the driver's apply_limit
276
+ # (which appends "LIMIT N OFFSET M") doesn't produce
277
+ # "SELECT * FROM t; LIMIT 100 OFFSET 0" — a syntax error
278
+ # on every engine. Also helps any COUNT(*) FROM (sql)
279
+ # subqueries downstream survive a user-supplied semicolon.
280
+ sql = Tina4::Database.strip_trailing_semicolons(sql)
281
+
256
282
  effective_sql = sql
257
283
  # Skip appending LIMIT if SQL already has one
258
284
  has_limit = sql.upcase.split("--")[0].include?("LIMIT")
@@ -279,6 +305,7 @@ module Tina4
279
305
  end
280
306
 
281
307
  def fetch_one(sql, params = [])
308
+ sql = Tina4::Database.strip_trailing_semicolons(sql)
282
309
  if @cache_enabled
283
310
  key = cache_key(sql + ":ONE", params)
284
311
  cached = cache_get(key)
data/lib/tina4/orm.rb CHANGED
@@ -18,7 +18,11 @@ module Tina4
18
18
 
19
19
  class << self
20
20
  def db
21
- @db || Tina4.database
21
+ # v3.13.12: implicit binding from TINA4_DATABASE_URL.
22
+ # Resolution: per-class @db → global Tina4.database → env-derived
23
+ # auto-discovery. Pre-v3.13.12 this fell through to nil — the
24
+ # helper auto_discover_db existed but was never called.
25
+ @db || Tina4.database || auto_discover_db
22
26
  end
23
27
 
24
28
  # Per-model database binding
@@ -302,13 +306,27 @@ module Tina4
302
306
  def create_table
303
307
  return true if db.table_exists?(table_name)
304
308
 
309
+ # v3.13.11 (BooleanField parity): pick each engine's native
310
+ # bool type where it's reliable. SQLite has no native bool;
311
+ # Firebird's driver round-trip for native BOOLEAN is uneven —
312
+ # both stay on INTEGER. PG/MySQL/MSSQL use their native types
313
+ # so Python ``True``/Ruby ``true`` bind cleanly without
314
+ # ``operator does not exist: boolean = integer`` errors.
315
+ engine = (db.respond_to?(:get_database_type) ? db.get_database_type : "").to_s.downcase
316
+ bool_sql = case engine
317
+ when "postgres", "postgresql" then "BOOLEAN"
318
+ when "mysql" then "BOOLEAN" # alias for TINYINT(1)
319
+ when "mssql", "sqlserver" then "BIT"
320
+ else "INTEGER" # sqlite, firebird, odbc, anything else
321
+ end
322
+
305
323
  type_map = {
306
324
  integer: "INTEGER",
307
325
  string: "VARCHAR(255)",
308
326
  text: "TEXT",
309
327
  float: "REAL",
310
328
  decimal: "REAL",
311
- boolean: "INTEGER",
329
+ boolean: bool_sql,
312
330
  date: "DATE",
313
331
  datetime: "DATETIME",
314
332
  timestamp: "TIMESTAMP",
@@ -419,10 +437,18 @@ module Tina4
419
437
  setter = "#{key}="
420
438
  __send__(setter, value) if respond_to?(setter)
421
439
  end
422
- # Set defaults
440
+ # Set defaults.
441
+ # v3.13.11 (issue #50.1): when the default is a Proc/lambda
442
+ # (``default: -> { Time.now }``), call it per-instance so
443
+ # per-row timestamps actually differ. Class objects are
444
+ # excluded — ``default: Integer`` is almost never intended
445
+ # to mean ``Integer.new`` (and Integer has no zero-arg
446
+ # constructor anyway).
423
447
  self.class.field_definitions.each do |name, opts|
424
448
  if __send__(name).nil? && opts[:default]
425
- __send__("#{name}=", opts[:default])
449
+ d = opts[:default]
450
+ d = d.call if d.respond_to?(:call) && !d.is_a?(Class)
451
+ __send__("#{name}=", d)
426
452
  end
427
453
  end
428
454
  end
data/lib/tina4/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.13.9"
4
+ VERSION = "3.13.12"
5
5
  end
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.9
4
+ version: 3.13.12
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-06-10 00:00:00.000000000 Z
11
+ date: 2026-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack