tina4ruby 3.10.1 → 3.10.3

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: 1111e4a55512ff1a1c0c01c82e4a180d107387b687ea8caf4b0753b4207f3f02
4
- data.tar.gz: 5abf4c9493fdaf45de25ed18f328f92595a484ec4e11b7ed1c87278a55748cb6
3
+ metadata.gz: cfe8b7f49e17e2a703725d23ce90791acca6e078dc7747a0eebaf97091121330
4
+ data.tar.gz: a6a2e0c2e48b69e332d6bc0358929a141f91919b19062639259d7c51cb390a39
5
5
  SHA512:
6
- metadata.gz: 91be26b34ba29c6cfe18038c3c1c78889c0723540483a663571f32482e3d6d7eff6a6fac44603627f516b21beba7f0355c656b1fce3545d6277cb294c52f027e
7
- data.tar.gz: 56597ac885f021695f130bde7924309592e1f69154a117276c19d0ca56b2165b7a0d77fbfb79669885225976895440ee9464909f765f1d5ffce31d7ea4f13c24
6
+ metadata.gz: 24a17f01e57ffda9278e80e0451e54f82111921c1066f21ab30436391e05c475c613c6948824f4fe8d94ddc17f39796240ca47f6ecda8bfe166f805b54335b22
7
+ data.tar.gz: 2b0c24c5dbef5d4f4d2c3e5337f7dce2bc1f8a89a90cbb149484fe375914cf8465e604ccc6f30e264a78922e4132ad27f7f4d7189e7f589450cf3baa5361c0f1
@@ -396,10 +396,24 @@ module Tina4
396
396
  end
397
397
 
398
398
  def resolve_variable(expr)
399
- parts = expr.split(".")
399
+ parts = split_dot_parts(expr)
400
400
  value = @context
401
401
  parts.each do |part|
402
- if part =~ /\A(\w+)\[(.+?)\]\z/
402
+ if part =~ /\A(\w+)\((.*)?\)\z/m
403
+ # Method call: e.g. t("key") or greet("hello", "world")
404
+ method_name = Regexp.last_match(1)
405
+ args_str = Regexp.last_match(2)
406
+ callable = access_value(value, method_name)
407
+ if callable.respond_to?(:call)
408
+ args = args_str && !args_str.strip.empty? ? parse_filter_args(args_str) : []
409
+ value = callable.call(*args)
410
+ elsif callable.respond_to?(method_name.to_sym)
411
+ args = args_str && !args_str.strip.empty? ? parse_filter_args(args_str) : []
412
+ value = callable.send(method_name.to_sym, *args)
413
+ else
414
+ return nil
415
+ end
416
+ elsif part =~ /\A(\w+)\[(.+?)\]\z/
403
417
  base = Regexp.last_match(1)
404
418
  index = Regexp.last_match(2)
405
419
  value = access_value(value, base)
@@ -417,6 +431,65 @@ module Tina4
417
431
  value
418
432
  end
419
433
 
434
+ # Split expression on dots, respecting quotes, parentheses and brackets.
435
+ # Dots inside quoted strings or nested parens/brackets are NOT separators.
436
+ # Bracket access like foo["bar"] is emitted as a separate part.
437
+ def split_dot_parts(expr)
438
+ parts = []
439
+ current = +""
440
+ paren_depth = 0
441
+ bracket_depth = 0
442
+ in_quote = nil
443
+
444
+ i = 0
445
+ chars = expr.chars
446
+ while i < chars.length
447
+ ch = chars[i]
448
+
449
+ if in_quote
450
+ current << ch
451
+ # End quote only when matching unescaped closer
452
+ in_quote = nil if ch == in_quote && (i == 0 || chars[i - 1] != "\\")
453
+ elsif ch == '"' || ch == "'"
454
+ in_quote = ch
455
+ current << ch
456
+ elsif ch == "("
457
+ paren_depth += 1
458
+ current << ch
459
+ elsif ch == ")"
460
+ paren_depth -= 1
461
+ current << ch
462
+ elsif ch == "[" && paren_depth == 0
463
+ if bracket_depth == 0 && !current.empty?
464
+ # Start of bracket access on an existing part -- split here
465
+ parts << current
466
+ current = +""
467
+ end
468
+ bracket_depth += 1
469
+ current << ch
470
+ elsif ch == "]"
471
+ bracket_depth -= 1
472
+ current << ch
473
+ if bracket_depth == 0 && paren_depth == 0
474
+ # End of top-level bracket access -- emit as its own part
475
+ parts << current
476
+ current = +""
477
+ # Skip a trailing dot that merely chains the next segment
478
+ i += 1 if i + 1 < chars.length && chars[i + 1] == "."
479
+ end
480
+ elsif ch == "." && paren_depth == 0 && bracket_depth == 0
481
+ parts << current unless current.empty?
482
+ current = +""
483
+ else
484
+ current << ch
485
+ end
486
+
487
+ i += 1
488
+ end
489
+ parts << current unless current.empty?
490
+ parts
491
+ end
492
+
420
493
  def access_value(obj, key)
421
494
  return nil if obj.nil?
422
495
  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.1"
4
+ VERSION = "3.10.3"
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.1
4
+ version: 3.10.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team