tina4ruby 3.10.55 → 3.10.65
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/README.md +2 -1
- data/lib/tina4/metrics.rb +55 -6
- data/lib/tina4/rack_app.rb +2 -1
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/webserver.rb +65 -4
- data/lib/tina4.rb +2 -1
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a6fa463a9244bec2d1144ebcdc903e19681ea7e60cc0b3ea3438e21b8388ec9e
|
|
4
|
+
data.tar.gz: 3ac6566d7b754979331b290e9c3610778eaa69ce6c31ae0eb8d8b16dd69deaa5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c40fcd4bc17ee1e0d2e41a297750210fc1e66708080a20c180dfe2f6338ce435d8e30042c0799a9455384b5808f221c00f156d13bdbd9aac4ecbbd655d9204fa
|
|
7
|
+
data.tar.gz: 180229e03f54704f3d3455d6b1c6690aea1979fbcbbfe2b05150c1831b25630628cbeb3b52cb7810f72939d9360e40d4a0ff8c42196df46591114aa923ea1e74
|
data/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
<img src="https://tina4.com/logo.svg" alt="Tina4" width="200">
|
|
3
3
|
</p>
|
|
4
4
|
<h1 align="center">Tina4 Ruby</h1>
|
|
5
|
-
<h3 align="center">
|
|
5
|
+
<h3 align="center">TINA4 — The Intelligent Native Application 4ramework</h3>
|
|
6
|
+
<p align="center"><em>Simple. Fast. Human. | Built for AI. Built for you.</em></p>
|
|
6
7
|
<p align="center">54 built-in features. Zero runtime dependencies. One require, everything works.</p>
|
|
7
8
|
<p align="center">
|
|
8
9
|
<a href="https://rubygems.org/gems/tina4ruby"><img src="https://img.shields.io/gem/v/tina4ruby?color=7b1fa2&label=RubyGems" alt="RubyGems"></a>
|
data/lib/tina4/metrics.rb
CHANGED
|
@@ -137,7 +137,7 @@ module Tina4
|
|
|
137
137
|
total_functions += functions
|
|
138
138
|
|
|
139
139
|
rel_path = begin
|
|
140
|
-
Pathname.new(f).relative_path_from(
|
|
140
|
+
Pathname.new(f).relative_path_from(root_path).to_s
|
|
141
141
|
rescue ArgumentError
|
|
142
142
|
f
|
|
143
143
|
end
|
|
@@ -238,7 +238,7 @@ module Tina4
|
|
|
238
238
|
end
|
|
239
239
|
|
|
240
240
|
rel_path = begin
|
|
241
|
-
Pathname.new(f).relative_path_from(
|
|
241
|
+
Pathname.new(f).relative_path_from(root_path).to_s
|
|
242
242
|
rescue ArgumentError
|
|
243
243
|
f
|
|
244
244
|
end
|
|
@@ -405,12 +405,61 @@ module Tina4
|
|
|
405
405
|
private_class_method
|
|
406
406
|
|
|
407
407
|
def self._has_matching_test(rel_path)
|
|
408
|
+
require 'set'
|
|
409
|
+
|
|
408
410
|
name = File.basename(rel_path, '.rb')
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
411
|
+
# Parent directory name (e.g. "database" from "database/sqlite3_adapter.rb")
|
|
412
|
+
parent_dir = File.dirname(rel_path)
|
|
413
|
+
parent_module = (parent_dir != '.' && !parent_dir.empty?) ? File.basename(parent_dir) : ''
|
|
414
|
+
|
|
415
|
+
# Stage 1: Filename matching — name_spec, name_test, test_name patterns
|
|
416
|
+
test_dirs = ['spec', 'spec/tina4', 'test', 'tests']
|
|
417
|
+
test_dirs.each do |td|
|
|
418
|
+
patterns = [
|
|
419
|
+
"#{td}/#{name}_spec.rb",
|
|
420
|
+
"#{td}/#{name}s_spec.rb",
|
|
421
|
+
"#{td}/#{name}_test.rb",
|
|
422
|
+
"#{td}/test_#{name}.rb",
|
|
423
|
+
]
|
|
424
|
+
# Also check parent-named tests (spec/database_spec.rb covers database/sqlite3_adapter.rb)
|
|
425
|
+
if parent_module && !parent_module.empty? && parent_module != name
|
|
426
|
+
patterns << "#{td}/#{parent_module}_spec.rb"
|
|
427
|
+
patterns << "#{td}/#{parent_module}s_spec.rb"
|
|
428
|
+
patterns << "#{td}/#{parent_module}_test.rb"
|
|
429
|
+
patterns << "#{td}/test_#{parent_module}.rb"
|
|
430
|
+
end
|
|
431
|
+
return true if patterns.any? { |p| File.exist?(p) }
|
|
413
432
|
end
|
|
433
|
+
|
|
434
|
+
# Build a dotted/slashed require path for import matching
|
|
435
|
+
# e.g. "lib/tina4/database/sqlite3_adapter.rb" → "tina4/database/sqlite3_adapter"
|
|
436
|
+
path_without_ext = rel_path.sub(/\.rb$/, '')
|
|
437
|
+
# Strip leading lib/ prefix if present
|
|
438
|
+
require_path = path_without_ext.sub(%r{^lib/}, '')
|
|
439
|
+
|
|
440
|
+
# Build CamelCase class name from snake_case module name
|
|
441
|
+
# e.g. "sqlite3_adapter" → "Sqlite3Adapter"
|
|
442
|
+
class_name = name.split('_').map(&:capitalize).join
|
|
443
|
+
|
|
444
|
+
# Stage 2+3: Content scan — check if any spec/test file references this module
|
|
445
|
+
scan_dirs = ['spec', 'test', 'tests']
|
|
446
|
+
scan_dirs.each do |td|
|
|
447
|
+
next unless Dir.exist?(td)
|
|
448
|
+
Dir.glob(File.join(td, '**', '*.rb')).each do |test_file|
|
|
449
|
+
content = begin
|
|
450
|
+
File.read(test_file, encoding: 'utf-8')
|
|
451
|
+
rescue StandardError
|
|
452
|
+
next
|
|
453
|
+
end
|
|
454
|
+
# Stage 2: require/require_relative path matching
|
|
455
|
+
return true if !require_path.empty? && content.include?(require_path)
|
|
456
|
+
# Stage 3: class name or module name mention
|
|
457
|
+
return true if content.match?(/\b#{Regexp.escape(class_name)}\b/)
|
|
458
|
+
return true if content.match?(/\b#{Regexp.escape(name)}\b/i)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
false
|
|
414
463
|
end
|
|
415
464
|
|
|
416
465
|
def self._files_hash(root)
|
data/lib/tina4/rack_app.rb
CHANGED
|
@@ -460,7 +460,8 @@ module Tina4
|
|
|
460
460
|
<div class="hero">
|
|
461
461
|
<img src="/images/tina4-logo-icon.webp" class="logo" alt="Tina4">
|
|
462
462
|
<h1>Tina4Ruby</h1>
|
|
463
|
-
<p class="tagline">
|
|
463
|
+
<p class="tagline">The Intelligent Native Application 4ramework</p>
|
|
464
|
+
<p class="tagline" style="font-size:0.95rem;margin-top:-1rem">Simple. Fast. Human. | Built for AI. Built for you.</p>
|
|
464
465
|
<div class="actions">
|
|
465
466
|
<a href="https://tina4.com/ruby" class="btn" target="_blank">Website</a>
|
|
466
467
|
<a href="/__dev" class="btn">Dev Admin</a>
|
data/lib/tina4/version.rb
CHANGED
data/lib/tina4/webserver.rb
CHANGED
|
@@ -8,10 +8,71 @@ module Tina4
|
|
|
8
8
|
@port = port
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# Kill whatever process is listening on *port*.
|
|
12
|
+
# Uses lsof on macOS/Linux and netstat + taskkill on Windows.
|
|
13
|
+
# Raises RuntimeError if the port cannot be freed.
|
|
14
|
+
def free_port(port)
|
|
15
|
+
puts " Port #{port} in use — killing existing process..."
|
|
16
|
+
|
|
17
|
+
if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
|
|
18
|
+
output = `netstat -ano 2>&1`
|
|
19
|
+
pid = nil
|
|
20
|
+
output.each_line do |line|
|
|
21
|
+
if line.include?(":#{port}") && (line.include?("LISTENING") || line.include?("ESTABLISHED"))
|
|
22
|
+
parts = line.strip.split(/\s+/)
|
|
23
|
+
candidate = parts.last
|
|
24
|
+
if candidate =~ /^\d+$/
|
|
25
|
+
pid = candidate
|
|
26
|
+
break
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
if pid
|
|
31
|
+
system("taskkill /PID #{pid} /F")
|
|
32
|
+
else
|
|
33
|
+
raise "Could not free port #{port}: no PID found"
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
pids = `lsof -ti :#{port} 2>/dev/null`.strip.split("\n")
|
|
37
|
+
if pids.empty?
|
|
38
|
+
return # Nothing found — port may have freed itself
|
|
39
|
+
end
|
|
40
|
+
pids.each do |pid|
|
|
41
|
+
pid = pid.strip
|
|
42
|
+
next unless pid =~ /^\d+$/
|
|
43
|
+
begin
|
|
44
|
+
Process.kill("TERM", pid.to_i)
|
|
45
|
+
rescue Errno::ESRCH
|
|
46
|
+
# Process already gone
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Give the OS a moment to reclaim the port
|
|
52
|
+
sleep(0.5)
|
|
53
|
+
puts " Port #{port} freed"
|
|
54
|
+
end
|
|
55
|
+
|
|
11
56
|
def start
|
|
12
57
|
require "webrick"
|
|
13
58
|
require "stringio"
|
|
14
59
|
require "socket"
|
|
60
|
+
|
|
61
|
+
# Ensure the main port is available — kill whatever is on it if needed
|
|
62
|
+
begin
|
|
63
|
+
test = TCPServer.new("0.0.0.0", @port)
|
|
64
|
+
test.close
|
|
65
|
+
rescue Errno::EADDRINUSE
|
|
66
|
+
free_port(@port)
|
|
67
|
+
# Verify the port is now free; raise if still occupied
|
|
68
|
+
begin
|
|
69
|
+
test = TCPServer.new("0.0.0.0", @port)
|
|
70
|
+
test.close
|
|
71
|
+
rescue Errno::EADDRINUSE
|
|
72
|
+
raise "Could not free port #{@port}"
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
15
76
|
Tina4.print_banner(host: @host, port: @port)
|
|
16
77
|
Tina4::Log.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
|
|
17
78
|
@server = WEBrick::HTTPServer.new(
|
|
@@ -103,14 +164,14 @@ module Tina4
|
|
|
103
164
|
|
|
104
165
|
@server.mount("/", servlet, rack_app)
|
|
105
166
|
|
|
106
|
-
#
|
|
167
|
+
# Test port (port + 1000) — stable, no-browser
|
|
107
168
|
@ai_server = nil
|
|
108
169
|
@ai_thread = nil
|
|
109
170
|
no_ai_port = %w[true 1 yes].include?(ENV.fetch("TINA4_NO_AI_PORT", "").downcase)
|
|
110
171
|
is_debug = %w[true 1 yes].include?(ENV.fetch("TINA4_DEBUG", "").downcase)
|
|
111
172
|
|
|
112
173
|
if is_debug && !no_ai_port
|
|
113
|
-
ai_port = @port +
|
|
174
|
+
ai_port = @port + 1000
|
|
114
175
|
begin
|
|
115
176
|
test = TCPServer.new("0.0.0.0", ai_port)
|
|
116
177
|
test.close
|
|
@@ -201,9 +262,9 @@ module Tina4
|
|
|
201
262
|
|
|
202
263
|
@ai_server.mount("/", ai_servlet, ai_rack_app)
|
|
203
264
|
@ai_thread = Thread.new { @ai_server.start }
|
|
204
|
-
puts "
|
|
265
|
+
puts " Test Port: http://localhost:#{ai_port} (stable — no hot-reload)"
|
|
205
266
|
rescue Errno::EADDRINUSE
|
|
206
|
-
puts "
|
|
267
|
+
puts " Test Port: SKIPPED (port #{ai_port} in use)"
|
|
207
268
|
end
|
|
208
269
|
end
|
|
209
270
|
|
data/lib/tina4.rb
CHANGED
|
@@ -138,7 +138,8 @@ module Tina4
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
puts "#{color}#{BANNER}#{reset}"
|
|
141
|
-
puts "
|
|
141
|
+
puts " TINA4 — The Intelligent Native Application 4ramework"
|
|
142
|
+
puts " Simple. Fast. Human. | Built for AI. Built for you."
|
|
142
143
|
puts ""
|
|
143
144
|
puts " Server: http://#{display}:#{port} (#{server_name})"
|
|
144
145
|
puts " Swagger: http://localhost:#{port}/swagger"
|
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.10.
|
|
4
|
+
version: 3.10.65
|
|
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-04-
|
|
11
|
+
date: 2026-04-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -262,8 +262,9 @@ dependencies:
|
|
|
262
262
|
- - "~>"
|
|
263
263
|
- !ruby/object:Gem::Version
|
|
264
264
|
version: '1.50'
|
|
265
|
-
description:
|
|
266
|
-
|
|
265
|
+
description: 'TINA4: The Intelligent Native Application 4ramework for Ruby — Simple.
|
|
266
|
+
Fast. Human. Built for AI. Built for you. 54 built-in features, zero dependencies.
|
|
267
|
+
Full parity with Python, PHP, and Node.js.'
|
|
267
268
|
email:
|
|
268
269
|
- info@tina4.com
|
|
269
270
|
executables:
|