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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 25b4d9017318ffe796da35f53d7865bd5ce5cad601a25d6dbffa512c649cd019
4
- data.tar.gz: 6ee84f47f431b714ff0b4ac84ff606c3406ca5e2896d17a0835b98f23ac9385f
3
+ metadata.gz: a6fa463a9244bec2d1144ebcdc903e19681ea7e60cc0b3ea3438e21b8388ec9e
4
+ data.tar.gz: 3ac6566d7b754979331b290e9c3610778eaa69ce6c31ae0eb8d8b16dd69deaa5
5
5
  SHA512:
6
- metadata.gz: eb0387a9321fb64193433c5d975bce3f7cfb2d25af20dcf9786cf361f6471cfecbbecad9a026e60f95f34446aa1fef65d77d93d19acb14ca7b1b6e615924cbb4
7
- data.tar.gz: 73a21f6cd92dda7b2a747d824b68685e827d87b4e42ae92fb2313e69af5fef4e3704e7bca70674045ed5f149926ff4a9fc2ae8a1de2ea2a6bd41e70d4de14aa6
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">This Is Now A 4Framework</h3>
5
+ <h3 align="center">TINA4 The Intelligent Native Application 4ramework</h3>
6
+ <p align="center"><em>Simple. Fast. Human. &nbsp;|&nbsp; 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(Pathname.new('.')).to_s
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(Pathname.new('.')).to_s
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
- ['spec', 'test'].any? do |dir|
410
- File.exist?("#{dir}/#{name}_spec.rb") ||
411
- File.exist?("#{dir}/#{name}_test.rb") ||
412
- File.exist?("#{dir}/test_#{name}.rb")
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)
@@ -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">This Is Now A 4Framework</p>
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. &nbsp;|&nbsp; 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tina4
4
- VERSION = "3.10.55"
4
+ VERSION = "3.10.65"
5
5
  end
@@ -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
- # AI dev port (port + 1) — no-reload, no-browser
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 + 1
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 " AI Port: http://localhost:#{ai_port} (no-reload)"
265
+ puts " Test Port: http://localhost:#{ai_port} (stable — no hot-reload)"
205
266
  rescue Errno::EADDRINUSE
206
- puts " AI Port: SKIPPED (port #{ai_port} in use)"
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 " Tina4 Ruby v#{VERSION} This Is Now A 4Framework"
141
+ puts " TINA4The 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.55
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-02 00:00:00.000000000 Z
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: Tina4 for Ruby 54 built-in features, zero dependencies. Full parity
266
- with Python, PHP, and Node.js.
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: