tina4ruby 3.10.13 → 3.10.15

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: c6d46cd66e041da43c47eef3869b8fd66455270fe1937d4c86ee839836dd6419
4
- data.tar.gz: bf4853aa8715cc865c25fa2693b10d50168d1fd48bc9a9371e8368a963e142bc
3
+ metadata.gz: 89bb63a5bf75c1e5100a6180e7283668bceb7a422e3dbfcb9ab175634ae0b8a5
4
+ data.tar.gz: 020d40942508338964f6e9c3b06417a11303f2997eb55827baefe59c9443bb11
5
5
  SHA512:
6
- metadata.gz: 21b443f74d40f46d610be7d039be8158b6a3a8be044476730ece168baaa2f8bb9b65a1b6e82f8a8c2d1cf50b9aafa614d99c3229b0c75b64b696973853cd1a21
7
- data.tar.gz: 93a487f54bcbf48995dc61915326b8826cac1a7a4250275ab0ec25fd3a47af55b39da60186dbeac86eb87db2c2d2b6b5f2a92f73f6ce46bf44db9c724cec8fb8
6
+ metadata.gz: 0c712a05318ae0cb27fa194121ff32999264835d27f7237e39cf3f80fd40b3b834b8e47a3a63c9605a43c0f503f284dc25bf3c3aa9441a69c14d192f382cacf2
7
+ data.tar.gz: bebefcf988a513b917ca9803474b07cf3dd6c116c5b3595a52d9582484c4fdfddd7e4b31472128012455f2d508d56866e378ef52809902d616ea5ba6a42a5c4d
@@ -311,6 +311,61 @@ module Tina4
311
311
  tables.any? { |t| t.downcase == table_name.to_s.downcase }
312
312
  end
313
313
 
314
+ # Pre-generate the next available primary key ID using engine-aware strategies.
315
+ #
316
+ # - Firebird: auto-creates a generator if missing, then increments via GEN_ID.
317
+ # - PostgreSQL: tries nextval() on the standard sequence, falls through to MAX+1.
318
+ # - SQLite/MySQL/MSSQL: uses MAX(pk) + 1.
319
+ # - Returns 1 if the table is empty or does not exist.
320
+ #
321
+ # @param table [String] Table name
322
+ # @param pk_column [String] Primary key column name (default: "id")
323
+ # @param generator_name [String, nil] Firebird generator name override
324
+ # @return [Integer] The next available ID
325
+ def get_next_id(table, pk_column: "id", generator_name: nil)
326
+ drv = current_driver
327
+
328
+ # Firebird — use generators
329
+ if @driver_name == "firebird"
330
+ gen_name = generator_name || "GEN_#{table.upcase}_ID"
331
+
332
+ # Auto-create the generator if it does not exist
333
+ begin
334
+ drv.execute("CREATE GENERATOR #{gen_name}")
335
+ rescue
336
+ # Generator already exists — ignore
337
+ end
338
+
339
+ rows = drv.execute_query("SELECT GEN_ID(#{gen_name}, 1) AS NEXT_ID FROM RDB$DATABASE")
340
+ row = rows.is_a?(Array) ? rows.first : nil
341
+ return (row && (row["NEXT_ID"] || row["next_id"]))&.to_i || 1
342
+ end
343
+
344
+ # PostgreSQL — try sequence first, fall through to MAX
345
+ if @driver_name == "postgres"
346
+ seq_name = "#{table.downcase}_#{pk_column.downcase}_seq"
347
+ begin
348
+ rows = drv.execute_query("SELECT nextval('#{seq_name}') AS next_id")
349
+ row = rows.is_a?(Array) ? rows.first : nil
350
+ if row && (row["next_id"] || row["nextval"])
351
+ return (row["next_id"] || row["nextval"]).to_i
352
+ end
353
+ rescue
354
+ # No sequence — fall through to MAX
355
+ end
356
+ end
357
+
358
+ # SQLite / MySQL / MSSQL / PostgreSQL fallback — MAX + 1
359
+ begin
360
+ rows = drv.execute_query("SELECT MAX(#{pk_column}) + 1 AS next_id FROM #{table}")
361
+ row = rows.is_a?(Array) ? rows.first : nil
362
+ next_id = row && (row["next_id"] || row["max"])
363
+ return next_id ? next_id.to_i : 1
364
+ rescue
365
+ return 1
366
+ end
367
+ end
368
+
314
369
  private
315
370
 
316
371
  def truthy?(val)
@@ -132,7 +132,10 @@ module Tina4
132
132
  "slice" => ->(v, s, e) { v.to_s[(s.to_i)..(e ? e.to_i : -1)] },
133
133
  "merge" => ->(v, o) { v.respond_to?(:merge) ? v.merge(o || {}) : v },
134
134
  "batch" => ->(v, s) { v.respond_to?(:each_slice) ? v.each_slice(s.to_i).to_a : [v] },
135
- "date" => ->(v, fmt) { TwigEngine.format_date(v, fmt) }
135
+ "date" => ->(v, fmt) { TwigEngine.format_date(v, fmt) },
136
+ "to_json" => ->(v) { JSON.generate(v).gsub("<", "\\u003c").gsub(">", "\\u003e").gsub("&", "\\u0026") rescue v.to_s },
137
+ "tojson" => ->(v) { JSON.generate(v).gsub("<", "\\u003c").gsub(">", "\\u003e").gsub("&", "\\u0026") rescue v.to_s },
138
+ "js_escape" => ->(v) { v.to_s.gsub("\\", "\\\\").gsub("'", "\\'").gsub('"', '\\"').gsub("\n", "\\n").gsub("\r", "\\r") }
136
139
  }.freeze
137
140
 
138
141
  def initialize(context = {}, base_dir = nil)
@@ -335,8 +338,15 @@ module Tina4
335
338
  args = []
336
339
  current = +""
337
340
  in_quote = nil
341
+ escaped = false
338
342
  args_str.each_char do |ch|
339
- if in_quote
343
+ if escaped
344
+ current << ch
345
+ escaped = false
346
+ elsif ch == "\\"
347
+ current << ch
348
+ escaped = true
349
+ elsif in_quote
340
350
  if ch == in_quote
341
351
  current << ch
342
352
  in_quote = nil
@@ -356,8 +366,8 @@ module Tina4
356
366
  args << current.strip unless current.strip.empty?
357
367
 
358
368
  args.map do |arg|
359
- if arg =~ /\A["'](.*)["']\z/
360
- Regexp.last_match(1)
369
+ if arg =~ /\A(["'])(.*)\1\z/m
370
+ process_escapes(Regexp.last_match(2))
361
371
  elsif arg =~ /\A\d+\z/
362
372
  arg.to_i
363
373
  elsif arg =~ /\A\d+\.\d+\z/
@@ -445,7 +455,9 @@ module Tina4
445
455
  expr = expr.strip
446
456
 
447
457
  # String literal early-return
448
- return Regexp.last_match(1) if expr =~ /\A"([^"]*)"\z/ || expr =~ /\A'([^']*)'\z/
458
+ if expr =~ /\A"([^"]*)"\z/ || expr =~ /\A'([^']*)'\z/
459
+ return process_escapes(Regexp.last_match(1))
460
+ end
449
461
 
450
462
  return expr.to_i if expr =~ /\A-?\d+\z/
451
463
  return expr.to_f if expr =~ /\A-?\d+\.\d+\z/
@@ -618,6 +630,30 @@ module Tina4
618
630
  parts
619
631
  end
620
632
 
633
+ # Process backslash escape sequences in a single pass so that
634
+ # \\' does not collapse to ' (it should become \').
635
+ def process_escapes(s)
636
+ out = +""
637
+ i = 0
638
+ while i < s.length
639
+ if s[i] == "\\" && i + 1 < s.length
640
+ nxt = s[i + 1]
641
+ case nxt
642
+ when "n" then out << "\n"; i += 2
643
+ when "t" then out << "\t"; i += 2
644
+ when "\\" then out << "\\"; i += 2
645
+ when "'" then out << "'"; i += 2
646
+ when '"' then out << '"'; i += 2
647
+ else out << "\\"; i += 1
648
+ end
649
+ else
650
+ out << s[i]
651
+ i += 1
652
+ end
653
+ end
654
+ out
655
+ end
656
+
621
657
  def access_value(obj, key)
622
658
  return nil if obj.nil?
623
659
  if obj.is_a?(Hash)
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.10.13"
4
+ VERSION = "3.10.15"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.10.13
4
+ version: 3.10.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team