livetext 0.9.60 → 0.9.62
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/livetext/core.rb +3 -3
- data/lib/livetext/function_caller.rb +36 -0
- data/lib/livetext/function_registry.rb +14 -2
- data/lib/livetext/functions.rb +1 -0
- data/lib/livetext/userapi.rb +7 -1
- data/lib/livetext/version.rb +1 -1
- data/test/snapshots/simple_func_api/expected-error.txt +0 -0
- data/test/snapshots/simple_func_api/match-output.txt +18 -0
- data/test/snapshots/simple_func_api/source.lt3 +32 -0
- data/test/snapshots/simple_func_api/stderr.txt +0 -0
- data/test/snapshots/system_info/match-output.txt +1 -1
- data/test/unit/all.rb +2 -0
- data/test/unit/function_api_access.rb +82 -0
- data/test/unit/function_caller.rb +102 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61c11b22070e565c5c25735ea1134d58249a5f441f0ed3c1f9eeb0f8bbf7c628
|
4
|
+
data.tar.gz: feff45c934b1ecb8757dcd1ea3fad4947e7383063faa9f3f68506dc73c99ff9e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e9fa6aeec991337d703e223f20daef4b65b0d261be3cdcfbce99854ab263c0660e2288de725411ba3eaa29c1e8e65ae421dddd433c187e813b10b052f658f1b
|
7
|
+
data.tar.gz: f899d4de12921521b8d06adb35d10ed1f530cab3f9bc1d8c665ddb9bd7eb54b397d6bf7bc0a8686ecdfbd71a9aba18b470da0da38f2687d09ca03ba662b30f7b
|
data/lib/livetext/core.rb
CHANGED
@@ -60,13 +60,13 @@ class Livetext
|
|
60
60
|
def initialize(output = ::STDOUT) # Livetext
|
61
61
|
@body = ""
|
62
62
|
@indentation = [0]
|
63
|
+
@function_registry = Livetext::FunctionRegistry.new
|
64
|
+
@variables = Livetext::VariableManager.new(self)
|
65
|
+
@formatter = Livetext::Formatter.new(self)
|
63
66
|
@api = UserAPI.new(self)
|
64
67
|
@output = ::Livetext.output = output
|
65
68
|
@html = Livetext::HTML.new(@api)
|
66
69
|
@sources = []
|
67
|
-
@function_registry = Livetext::FunctionRegistry.new
|
68
|
-
@variables = Livetext::VariableManager.new(self)
|
69
|
-
@formatter = Livetext::Formatter.new(self)
|
70
70
|
# puts "------ init: self = "
|
71
71
|
# p self
|
72
72
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# FunctionCaller - Provides simple method-style access to Livetext functions
|
2
|
+
class Livetext::FunctionCaller
|
3
|
+
def initialize(function_registry, api)
|
4
|
+
@registry = function_registry
|
5
|
+
@api = api
|
6
|
+
end
|
7
|
+
|
8
|
+
# Dynamically handle method calls to function names
|
9
|
+
def method_missing(name, *args)
|
10
|
+
# Convert method name to string and call the function registry
|
11
|
+
function_name = name.to_s
|
12
|
+
param = args.first || ""
|
13
|
+
|
14
|
+
# Set api on Livetext::Functions so all functions can access it
|
15
|
+
Livetext::Functions.api = @api
|
16
|
+
|
17
|
+
@registry.call(function_name, param)
|
18
|
+
rescue => e
|
19
|
+
"[Error calling function #{function_name}: #{e.message}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Check if a function exists
|
23
|
+
def respond_to_missing?(name, include_private = false)
|
24
|
+
@registry.function_exists?(name.to_s) || super
|
25
|
+
end
|
26
|
+
|
27
|
+
# List all available functions
|
28
|
+
def list
|
29
|
+
@registry.list_functions.map { |f| f[:name] }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Check if a specific function exists
|
33
|
+
def exists?(name)
|
34
|
+
@registry.function_exists?(name.to_s)
|
35
|
+
end
|
36
|
+
end
|
@@ -5,7 +5,7 @@ class Livetext::FunctionRegistry
|
|
5
5
|
@builtin_functions = {}
|
6
6
|
@metadata = {}
|
7
7
|
register_builtin_functions
|
8
|
-
puts "DEBUG: Registered #{@builtin_functions.size} builtin functions" if ENV['DEBUG']
|
8
|
+
# puts "DEBUG: Registered #{@builtin_functions.size} builtin functions" if ENV['DEBUG']
|
9
9
|
end
|
10
10
|
|
11
11
|
def register_user(name, function, source: :inline, filename: nil)
|
@@ -28,6 +28,18 @@ class Livetext::FunctionRegistry
|
|
28
28
|
elsif @builtin_functions[name_sym]
|
29
29
|
call_function(name, @builtin_functions[name_sym], param)
|
30
30
|
else
|
31
|
+
# Fall back to Livetext::Functions for backward compatibility
|
32
|
+
fobj = ::Livetext::Functions.new
|
33
|
+
if fobj.respond_to?(name_sym)
|
34
|
+
method = fobj.method(name_sym)
|
35
|
+
if method.parameters.empty?
|
36
|
+
result = fobj.send(name_sym)
|
37
|
+
else
|
38
|
+
result = fobj.send(name_sym, param)
|
39
|
+
end
|
40
|
+
return result.to_s if result
|
41
|
+
end
|
42
|
+
|
31
43
|
"[Error evaluating $$#{name}(#{param})]"
|
32
44
|
end
|
33
45
|
end
|
@@ -98,7 +110,7 @@ class Livetext::FunctionRegistry
|
|
98
110
|
end
|
99
111
|
end)
|
100
112
|
register_builtin(:br, ->(param) do
|
101
|
-
n = (param
|
113
|
+
n = (param && !param.empty?) ? param.to_i : 1
|
102
114
|
"<br>" * n
|
103
115
|
end)
|
104
116
|
register_builtin(:reverse, ->(param) do
|
data/lib/livetext/functions.rb
CHANGED
data/lib/livetext/userapi.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require_relative 'expansion'
|
2
2
|
require_relative 'html'
|
3
|
+
require_relative 'function_caller'
|
3
4
|
|
4
5
|
# Encapsulate the UserAPI as a class
|
5
6
|
|
@@ -19,6 +20,7 @@ class Livetext::UserAPI
|
|
19
20
|
@vars = live.vars
|
20
21
|
@html = Livetext::HTML.new(self)
|
21
22
|
@expander = Livetext::Expansion.new(live)
|
23
|
+
@funcs = Livetext::FunctionCaller.new(live.function_registry, self)
|
22
24
|
end
|
23
25
|
|
24
26
|
def api
|
@@ -53,7 +55,7 @@ class Livetext::UserAPI
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def expand_functions(str)
|
56
|
-
@expander.
|
58
|
+
@expander.expand_function_calls(str)
|
57
59
|
end
|
58
60
|
|
59
61
|
def setvar(var, val) # FIXME
|
@@ -244,5 +246,9 @@ class Livetext::UserAPI
|
|
244
246
|
def debug(*args)
|
245
247
|
TTY.puts *args if @live.debug
|
246
248
|
end
|
249
|
+
|
250
|
+
def funcs
|
251
|
+
@funcs
|
252
|
+
end
|
247
253
|
end
|
248
254
|
|
data/lib/livetext/version.rb
CHANGED
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
1 <h3>Testing Simple Function API</h3>
|
2
|
+
2 <p>Date: 2025-09-09</p>
|
3
|
+
3 /<p>Time: \d{2}:\d{2}:\d{2}</p>/
|
4
|
+
4 /<p>Directory: .*livetext.*</p>/
|
5
|
+
5 <p>Platform: universal.x86_64-darwin22</p>
|
6
|
+
6 <p>Ruby version: 2.6.10</p>
|
7
|
+
7 <p>Livetext version: 0.9.61</p>
|
8
|
+
8 <p>Reverse of 'hello': olleh</p>
|
9
|
+
9 <p>Square root of 16: 4</p>
|
10
|
+
10 /<p>Random \(1-10\): \d+</p>/
|
11
|
+
11 /<p>Link: <a style='text-decoration: none' href='https://google\.com'>Google</a></p>/
|
12
|
+
12 <p>Line breaks: <br><br></p>
|
13
|
+
13 <p>Date exists: true</p>
|
14
|
+
14 <p>Nonexistent exists: false</p>
|
15
|
+
15 <p>Total functions: 23</p>
|
16
|
+
16 /<p>First 5 functions: br, date, day, days_ago, days_from_now</p>/
|
17
|
+
17 <p><strong>Simple API works!</strong></p>
|
18
|
+
18 </p>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
.dot_def test_simple_func_api args
|
2
|
+
api.out "<h3>Testing Simple Function API</h3>"
|
3
|
+
|
4
|
+
# Test basic function calls
|
5
|
+
api.out "<p>Date: #{api.funcs.date()}</p>"
|
6
|
+
api.out "<p>Time: #{api.funcs.time()}</p>"
|
7
|
+
api.out "<p>Directory: #{api.funcs.pwd()}</p>"
|
8
|
+
api.out "<p>Platform: #{api.funcs.platform()}</p>"
|
9
|
+
api.out "<p>Ruby version: #{api.funcs.ruby_version()}</p>"
|
10
|
+
api.out "<p>Livetext version: #{api.funcs.livetext_version()}</p>"
|
11
|
+
|
12
|
+
# Test functions with parameters
|
13
|
+
api.out "<p>Reverse of 'hello': #{api.funcs.reverse('hello')}</p>"
|
14
|
+
api.out "<p>Square root of 16: #{api.funcs.isqrt('16')}</p>"
|
15
|
+
api.out "<p>Random (1-10): #{api.funcs.rand('1 10')}</p>"
|
16
|
+
api.out "<p>Link: #{api.funcs.link('Google|https://google.com')}</p>"
|
17
|
+
api.out "<p>Line breaks: #{api.funcs.br('2')}</p>"
|
18
|
+
|
19
|
+
# Test function existence
|
20
|
+
api.out "<p>Date exists: #{api.funcs.exists?('date')}</p>"
|
21
|
+
api.out "<p>Nonexistent exists: #{api.funcs.exists?('nonexistent')}</p>"
|
22
|
+
|
23
|
+
# Test function listing
|
24
|
+
functions = api.funcs.list
|
25
|
+
api.out "<p>Total functions: #{functions.length}</p>"
|
26
|
+
api.out "<p>First 5 functions: #{functions.first(5).join(', ')}</p>"
|
27
|
+
|
28
|
+
api.out "<p><strong>Simple API works!</strong></p>"
|
29
|
+
.end
|
30
|
+
|
31
|
+
.test_simple_func_api
|
32
|
+
|
File without changes
|
data/test/unit/all.rb
CHANGED
@@ -10,6 +10,8 @@ require_relative 'variables'
|
|
10
10
|
require_relative 'variable_manager'
|
11
11
|
require_relative 'functions'
|
12
12
|
require_relative 'function_registry'
|
13
|
+
require_relative 'function_caller'
|
14
|
+
require_relative 'function_api_access'
|
13
15
|
require_relative 'mixin_functions_class'
|
14
16
|
require_relative 'formatter'
|
15
17
|
require_relative 'formatter_component'
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/livetext'
|
3
|
+
|
4
|
+
class TestFunctionAPIAccess < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@live = Livetext.new
|
7
|
+
@live.vars.set(:View, "test_view")
|
8
|
+
@live.vars.set(:"post.id", "0001")
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_scriptorium_image_scenario
|
12
|
+
# This test mimics the exact scenario from Scriptorium's image function
|
13
|
+
# Define the asset function directly in Livetext::Functions (like in Scriptorium)
|
14
|
+
Livetext::Functions.class_eval do
|
15
|
+
def asset(param)
|
16
|
+
# Get view and post information (exactly like in Scriptorium)
|
17
|
+
vname = self.class.api.vars.to_h[:View]
|
18
|
+
postid = self.class.api.vars.to_h[:"post.id"]
|
19
|
+
"assets/#{postid}/#{param}" # Simplified version of the asset function
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Test calling it through api.funcs (like the image function in Scriptorium)
|
24
|
+
result = @live.api.funcs.asset("test.jpg")
|
25
|
+
|
26
|
+
assert_equal "assets/0001/test.jpg", result
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_function_can_access_api_vars
|
30
|
+
# Define a function that needs access to api.vars (like the asset function in Scriptorium)
|
31
|
+
Livetext::Functions.class_eval do
|
32
|
+
def test_asset_function(param)
|
33
|
+
# This mimics the asset function from Scriptorium that needs api.vars
|
34
|
+
vname = self.class.api.vars.to_h[:View]
|
35
|
+
postid = self.class.api.vars.to_h[:"post.id"]
|
36
|
+
"view=#{vname}, post=#{postid}, file=#{param}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Test calling it through api.funcs (like the image function in Scriptorium)
|
41
|
+
result = @live.api.funcs.test_asset_function("test.jpg")
|
42
|
+
|
43
|
+
assert_equal "view=test_view, post=0001, file=test.jpg", result
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_function_can_access_api_vars_without_params
|
47
|
+
# Define a function that needs access to api.vars but takes no parameters
|
48
|
+
Livetext::Functions.class_eval do
|
49
|
+
def test_view_info
|
50
|
+
vname = self.class.api.vars.to_h[:View]
|
51
|
+
postid = self.class.api.vars.to_h[:"post.id"]
|
52
|
+
"view=#{vname}, post=#{postid}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Test calling it through api.funcs
|
57
|
+
result = @live.api.funcs.test_view_info()
|
58
|
+
|
59
|
+
assert_equal "view=test_view, post=0001", result
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_function_handles_missing_api_gracefully
|
63
|
+
# Define a function that tries to access api but it's not set
|
64
|
+
Livetext::Functions.class_eval do
|
65
|
+
def test_no_api
|
66
|
+
if self.class.api
|
67
|
+
"api available"
|
68
|
+
else
|
69
|
+
"no api"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Clear the api to test the fallback
|
75
|
+
Livetext::Functions.api = nil
|
76
|
+
|
77
|
+
# Test calling it through api.funcs - should still work because we set it in method_missing
|
78
|
+
result = @live.api.funcs.test_no_api()
|
79
|
+
|
80
|
+
assert_equal "api available", result
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../../lib/livetext'
|
3
|
+
require_relative '../../lib/livetext/function_caller'
|
4
|
+
|
5
|
+
class TestFunctionCaller < Minitest::Test
|
6
|
+
def setup
|
7
|
+
@registry = Livetext::FunctionRegistry.new
|
8
|
+
@api = Object.new # Mock api object for testing
|
9
|
+
@caller = Livetext::FunctionCaller.new(@registry, @api)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_calls_builtin_functions
|
13
|
+
# Test date function
|
14
|
+
result = @caller.date
|
15
|
+
assert_match(/\d{4}-\d{2}-\d{2}/, result)
|
16
|
+
|
17
|
+
# Test time function
|
18
|
+
result = @caller.time
|
19
|
+
assert_match(/\d{2}:\d{2}:\d{2}/, result)
|
20
|
+
|
21
|
+
# Test pwd function
|
22
|
+
result = @caller.pwd
|
23
|
+
assert_equal(Dir.pwd, result)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_calls_functions_with_parameters
|
27
|
+
# Test reverse function
|
28
|
+
result = @caller.reverse("hello")
|
29
|
+
assert_equal("olleh", result)
|
30
|
+
|
31
|
+
# Test isqrt function
|
32
|
+
result = @caller.isqrt("16")
|
33
|
+
assert_equal("4", result)
|
34
|
+
|
35
|
+
# Test rand function
|
36
|
+
result = @caller.rand("1 10")
|
37
|
+
num = result.to_i
|
38
|
+
assert(num >= 1 && num <= 10, "Random number should be between 1 and 10, got #{result}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_calls_functions_with_no_parameters
|
42
|
+
# Test platform function
|
43
|
+
result = @caller.platform
|
44
|
+
assert_equal(RUBY_PLATFORM, result)
|
45
|
+
|
46
|
+
# Test ruby_version function
|
47
|
+
result = @caller.ruby_version
|
48
|
+
assert_equal(RUBY_VERSION, result)
|
49
|
+
|
50
|
+
# Test livetext_version function
|
51
|
+
result = @caller.livetext_version
|
52
|
+
assert_equal(Livetext::VERSION, result)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_handles_nonexistent_functions
|
56
|
+
result = @caller.nonexistent_function("param")
|
57
|
+
assert_match(/Error evaluating \$\$nonexistent_function/, result)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_function_existence_check
|
61
|
+
assert(@caller.exists?("date"))
|
62
|
+
assert(@caller.exists?("time"))
|
63
|
+
assert(@caller.exists?("pwd"))
|
64
|
+
refute(@caller.exists?("nonexistent"))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_list_functions
|
68
|
+
functions = @caller.list
|
69
|
+
assert_instance_of(Array, functions)
|
70
|
+
assert(functions.include?("date"))
|
71
|
+
assert(functions.include?("time"))
|
72
|
+
assert(functions.include?("pwd"))
|
73
|
+
assert(functions.include?("reverse"))
|
74
|
+
assert(functions.include?("isqrt"))
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_respond_to_missing
|
78
|
+
assert(@caller.respond_to?(:date))
|
79
|
+
assert(@caller.respond_to?(:time))
|
80
|
+
assert(@caller.respond_to?(:pwd))
|
81
|
+
refute(@caller.respond_to?(:nonexistent))
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_link_function
|
85
|
+
result = @caller.link("Google|https://google.com")
|
86
|
+
expected = "<a style='text-decoration: none' href='https://google.com'>Google</a>"
|
87
|
+
assert_equal(expected, result)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_br_function
|
91
|
+
result = @caller.br("3")
|
92
|
+
assert_equal("<br><br><br>", result)
|
93
|
+
|
94
|
+
# Test with empty parameter (default to 1)
|
95
|
+
result = @caller.br("")
|
96
|
+
assert_equal("<br>", result)
|
97
|
+
|
98
|
+
# Test with nil parameter (default to 1)
|
99
|
+
result = @caller.br(nil)
|
100
|
+
assert_equal("<br>", result)
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: livetext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.62
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hal Fulton
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-09-
|
10
|
+
date: 2025-09-09 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: A smart text processor extensible in Ruby
|
13
13
|
email: rubyhacker@gmail.com
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- lib/livetext/expansion.rb
|
36
36
|
- lib/livetext/formatter.rb
|
37
37
|
- lib/livetext/formatter_component.rb
|
38
|
+
- lib/livetext/function_caller.rb
|
38
39
|
- lib/livetext/function_registry.rb
|
39
40
|
- lib/livetext/functions.rb
|
40
41
|
- lib/livetext/global_helpers.rb
|
@@ -197,6 +198,10 @@ files:
|
|
197
198
|
- test/snapshots/simple_copy/expected-output.txt
|
198
199
|
- test/snapshots/simple_copy/simplefile.inc
|
199
200
|
- test/snapshots/simple_copy/source.lt3
|
201
|
+
- test/snapshots/simple_func_api/expected-error.txt
|
202
|
+
- test/snapshots/simple_func_api/match-output.txt
|
203
|
+
- test/snapshots/simple_func_api/source.lt3
|
204
|
+
- test/snapshots/simple_func_api/stderr.txt
|
200
205
|
- test/snapshots/simple_import/expected-error.txt
|
201
206
|
- test/snapshots/simple_import/expected-output.txt
|
202
207
|
- test/snapshots/simple_import/simple_import.rb
|
@@ -236,6 +241,8 @@ files:
|
|
236
241
|
- test/unit/double.rb
|
237
242
|
- test/unit/formatter.rb
|
238
243
|
- test/unit/formatter_component.rb
|
244
|
+
- test/unit/function_api_access.rb
|
245
|
+
- test/unit/function_caller.rb
|
239
246
|
- test/unit/function_registry.rb
|
240
247
|
- test/unit/functions.rb
|
241
248
|
- test/unit/html.rb
|