tina4ruby 3.13.26 → 3.13.29
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/docs.rb +67 -4
- data/lib/tina4/frond.rb +26 -8
- data/lib/tina4/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 01fb453b8f6a6ea027dc04bc3e1d56c808969f50739ca60946152030883e7fd0
|
|
4
|
+
data.tar.gz: b04d9e2902f61df6e37ae6eb81b95b6f60edf579739206ea97b9463a1ba5dd25
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c3ce7f5a22fef09964273c1645a3b1756a2359bf2d670bcc2b3c640bd8f0689a38653dd046b12c000fdc78ce9cbbb0dec386317a5d13e3cf09243c39461888f
|
|
7
|
+
data.tar.gz: dca9b42a7753fd2609d263867846d236e1c3e10b094f37a6030c690cadab75e3041b787e886c6feceb7dbafcb73b8bf3ba090c60fc69167e3309d1f3b79d378c
|
data/lib/tina4/docs.rb
CHANGED
|
@@ -98,8 +98,7 @@ module Tina4
|
|
|
98
98
|
# Full reflection of a single class — `nil` for unknown.
|
|
99
99
|
def class_spec(fqn)
|
|
100
100
|
ensure_index
|
|
101
|
-
|
|
102
|
-
class_entry = all_entries.find { |e| e[:kind] == "class" && e[:fqn] == key }
|
|
101
|
+
class_entry = resolve_class(fqn)
|
|
103
102
|
return nil if class_entry.nil?
|
|
104
103
|
|
|
105
104
|
methods = all_entries.select do |m|
|
|
@@ -123,9 +122,10 @@ module Tina4
|
|
|
123
122
|
# Single method spec — `nil` for unknown.
|
|
124
123
|
def method_spec(class_fqn, method_name)
|
|
125
124
|
ensure_index
|
|
126
|
-
|
|
125
|
+
klass = resolve_class(class_fqn)
|
|
126
|
+
resolved = klass ? klass[:fqn] : normalise_fqn(class_fqn)
|
|
127
127
|
entry = all_entries.find do |e|
|
|
128
|
-
e[:kind] == "method" && e[:class_fqn] ==
|
|
128
|
+
e[:kind] == "method" && e[:class_fqn] == resolved && e[:name] == method_name.to_s
|
|
129
129
|
end
|
|
130
130
|
entry && method_payload(entry)
|
|
131
131
|
end
|
|
@@ -558,6 +558,38 @@ module Tina4
|
|
|
558
558
|
tokens.each do |tk|
|
|
559
559
|
score += 1 if !tk.empty? && doc.include?(tk)
|
|
560
560
|
end
|
|
561
|
+
|
|
562
|
+
# Class-qualified queries ("Frond.add_test" / "Frond::add_test" /
|
|
563
|
+
# "Frond add_test"): score the owning class so the qualifier steers
|
|
564
|
+
# ranking instead of being dead weight.
|
|
565
|
+
class_fqn = (entry[:class_fqn] || entry[:class]).to_s
|
|
566
|
+
unless class_fqn.empty?
|
|
567
|
+
parent = class_fqn.split("::").last.to_s.downcase
|
|
568
|
+
unless parent.empty?
|
|
569
|
+
# Exact "Class.method" intent — normalise `.`/`::`/whitespace in the
|
|
570
|
+
# (already space-stripped) joined query to a single `.` separator.
|
|
571
|
+
q_norm = joined.gsub(/[:.]+/, ".")
|
|
572
|
+
if q_norm == "#{parent}.#{name}" || q_norm == "#{parent}.#{stripped}"
|
|
573
|
+
score += 6 # strongest signal we have
|
|
574
|
+
end
|
|
575
|
+
tokens.each do |tk|
|
|
576
|
+
next if tk.empty?
|
|
577
|
+
if tk == parent
|
|
578
|
+
score += 2.5
|
|
579
|
+
elsif parent.start_with?(tk)
|
|
580
|
+
score += 1
|
|
581
|
+
end
|
|
582
|
+
end
|
|
583
|
+
end
|
|
584
|
+
end
|
|
585
|
+
|
|
586
|
+
# Any token that is a whole dotted / :: segment of the fqn
|
|
587
|
+
# (namespace / class / method).
|
|
588
|
+
fqn_segs = entry[:fqn].to_s.downcase.split(/[.:#\s]+/).reject(&:empty?)
|
|
589
|
+
tokens.each do |tk|
|
|
590
|
+
score += 1 if !tk.empty? && fqn_segs.include?(tk)
|
|
591
|
+
end
|
|
592
|
+
|
|
561
593
|
# Substring fallback — full joined query inside the name.
|
|
562
594
|
if !joined.empty? && score.zero? && name.include?(joined)
|
|
563
595
|
score += 2
|
|
@@ -632,5 +664,36 @@ module Tina4
|
|
|
632
664
|
def normalise_fqn(fqn)
|
|
633
665
|
fqn.to_s.sub(/\A::/, "")
|
|
634
666
|
end
|
|
667
|
+
|
|
668
|
+
# Resolve a class entry by exact FQN, documented public path, or bare name.
|
|
669
|
+
#
|
|
670
|
+
# Callers naturally type the exact stored FQN (`Tina4::Database`), a bare
|
|
671
|
+
# class name (`Database`), or a longer path that nests the class name
|
|
672
|
+
# (`Tina4::Database::Database`). Match exactly first, then by class name
|
|
673
|
+
# (the last `::`/`.` segment), disambiguating by requiring the given
|
|
674
|
+
# segments to appear in the stored FQN (framework + shortest wins). A
|
|
675
|
+
# genuinely unknown name returns `nil` — no false positives.
|
|
676
|
+
def resolve_class(given)
|
|
677
|
+
classes = all_entries.select { |e| e[:kind] == "class" }
|
|
678
|
+
key = normalise_fqn(given)
|
|
679
|
+
|
|
680
|
+
exact = classes.find { |e| e[:fqn] == key }
|
|
681
|
+
return exact if exact
|
|
682
|
+
|
|
683
|
+
gsegs = key.split(/[:.]+/).reject(&:empty?)
|
|
684
|
+
gname = gsegs.empty? ? key : gsegs.last
|
|
685
|
+
cands = classes.select { |e| e[:fqn].to_s.split(/[:.]+/).last == gname }
|
|
686
|
+
return nil if cands.empty?
|
|
687
|
+
return cands.first if cands.size == 1
|
|
688
|
+
|
|
689
|
+
# Disambiguate: prefer classes whose stored FQN contains every given
|
|
690
|
+
# segment, then framework source, then the shortest FQN.
|
|
691
|
+
subset = cands.select do |e|
|
|
692
|
+
segs = e[:fqn].to_s.split(/[:.]+/)
|
|
693
|
+
gsegs.all? { |s| segs.include?(s) }
|
|
694
|
+
end
|
|
695
|
+
pool = subset.empty? ? cands : subset
|
|
696
|
+
pool.min_by { |e| [e[:source] == "framework" ? 0 : 1, e[:fqn].to_s.length, e[:fqn].to_s] }
|
|
697
|
+
end
|
|
635
698
|
end
|
|
636
699
|
end
|
data/lib/tina4/frond.rb
CHANGED
|
@@ -10,6 +10,7 @@ require "json"
|
|
|
10
10
|
require "digest"
|
|
11
11
|
require "base64"
|
|
12
12
|
require "cgi"
|
|
13
|
+
require "erb"
|
|
13
14
|
require "uri"
|
|
14
15
|
require "date"
|
|
15
16
|
require "time"
|
|
@@ -738,7 +739,7 @@ module Tina4
|
|
|
738
739
|
when "title" then value.to_s.split.map(&:capitalize).join(" ")
|
|
739
740
|
when "string" then value.to_s
|
|
740
741
|
when "int" then value.to_i
|
|
741
|
-
when "escape", "e" then Frond.escape_html(value.to_s)
|
|
742
|
+
when "escape", "e" then Tina4::SafeString.new(Frond.escape_html(value.to_s))
|
|
742
743
|
else value
|
|
743
744
|
end
|
|
744
745
|
next
|
|
@@ -1042,9 +1043,26 @@ module Tina4
|
|
|
1042
1043
|
# ── Literal values: strings, numbers, booleans, null ──
|
|
1043
1044
|
|
|
1044
1045
|
def eval_literal(expr)
|
|
1045
|
-
if
|
|
1046
|
-
|
|
1047
|
-
|
|
1046
|
+
if expr.length >= 2 && (expr[0] == '"' || expr[0] == "'") && expr[-1] == expr[0]
|
|
1047
|
+
# Only a SINGLE complete string literal — i.e. the opening quote's
|
|
1048
|
+
# match is the final char. Without this check, `'a' ~ 'b'` and
|
|
1049
|
+
# `'Y' if x else 'N'` (which merely start and end with a quote) get
|
|
1050
|
+
# their outer quotes stripped here before concat / inline-if run.
|
|
1051
|
+
q = expr[0]
|
|
1052
|
+
i = 1
|
|
1053
|
+
single = false
|
|
1054
|
+
while i < expr.length
|
|
1055
|
+
if expr[i] == "\\"
|
|
1056
|
+
i += 2
|
|
1057
|
+
next
|
|
1058
|
+
end
|
|
1059
|
+
if expr[i] == q
|
|
1060
|
+
single = (i == expr.length - 1)
|
|
1061
|
+
break
|
|
1062
|
+
end
|
|
1063
|
+
i += 1
|
|
1064
|
+
end
|
|
1065
|
+
return expr[1..-2] if single
|
|
1048
1066
|
end
|
|
1049
1067
|
return expr.to_i if expr =~ INTEGER_RE
|
|
1050
1068
|
return expr.to_f if expr =~ FLOAT_RE
|
|
@@ -1894,8 +1912,8 @@ module Tina4
|
|
|
1894
1912
|
"striptags" => ->(v, *_a) { v.to_s.gsub(STRIPTAGS_RE, "") },
|
|
1895
1913
|
|
|
1896
1914
|
# -- Encoding --
|
|
1897
|
-
"escape" => ->(v, *_a) { Frond.escape_html(v.to_s) },
|
|
1898
|
-
"e" => ->(v, *_a) { Frond.escape_html(v.to_s) },
|
|
1915
|
+
"escape" => ->(v, *_a) { Tina4::SafeString.new(Frond.escape_html(v.to_s)) },
|
|
1916
|
+
"e" => ->(v, *_a) { Tina4::SafeString.new(Frond.escape_html(v.to_s)) },
|
|
1899
1917
|
"raw" => ->(v, *_a) { v },
|
|
1900
1918
|
"safe" => ->(v, *_a) { v },
|
|
1901
1919
|
"json_encode" => ->(v, *_a) { JSON.generate(v) rescue v.to_s },
|
|
@@ -1914,7 +1932,7 @@ module Tina4
|
|
|
1914
1932
|
v.to_s
|
|
1915
1933
|
end
|
|
1916
1934
|
},
|
|
1917
|
-
"url_encode" => ->(v, *_a) {
|
|
1935
|
+
"url_encode" => ->(v, *_a) { ERB::Util.url_encode(v.to_s) },
|
|
1918
1936
|
|
|
1919
1937
|
# -- JSON / JS --
|
|
1920
1938
|
"to_json" => ->(v, *a) {
|
|
@@ -2058,7 +2076,7 @@ module Tina4
|
|
|
2058
2076
|
lines.join("\n")
|
|
2059
2077
|
},
|
|
2060
2078
|
"slug" => ->(v, *_a) { v.to_s.downcase.gsub(SLUG_CLEAN_RE, "-").gsub(SLUG_TRIM_RE, "") },
|
|
2061
|
-
"nl2br" => ->(v, *_a) { v.to_s.gsub("\n", "<br
|
|
2079
|
+
"nl2br" => ->(v, *_a) { Tina4::SafeString.new(Frond.escape_html(v.to_s).gsub("\n", "<br />\n")) },
|
|
2062
2080
|
"format" => ->(v, *a) {
|
|
2063
2081
|
if a.any?
|
|
2064
2082
|
v.to_s % a
|
data/lib/tina4/version.rb
CHANGED