livetext 0.9.52 → 0.9.55

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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/imports/bookish.rb +3 -3
  3. data/lib/livetext/core.rb +108 -53
  4. data/lib/livetext/expansion.rb +21 -21
  5. data/lib/livetext/formatter.rb +70 -200
  6. data/lib/livetext/formatter_component.rb +189 -0
  7. data/lib/livetext/function_registry.rb +163 -0
  8. data/lib/livetext/handler/mixin.rb +25 -0
  9. data/lib/livetext/helpers.rb +25 -7
  10. data/lib/livetext/reopen.rb +2 -0
  11. data/lib/livetext/skeleton.rb +0 -3
  12. data/lib/livetext/standard.rb +118 -70
  13. data/lib/livetext/variable_manager.rb +65 -0
  14. data/lib/livetext/variables.rb +4 -0
  15. data/lib/livetext/version.rb +1 -1
  16. data/lib/livetext.rb +9 -3
  17. data/plugin/booktool.rb +8 -8
  18. data/test/snapshots/complex_body/expected-error.txt +0 -0
  19. data/test/snapshots/complex_body/expected-output.txt +8 -0
  20. data/test/snapshots/complex_body/source.lt3 +19 -0
  21. data/test/snapshots/debug_command/expected-error.txt +0 -0
  22. data/test/snapshots/debug_command/expected-output.txt +1 -0
  23. data/test/snapshots/debug_command/source.lt3 +3 -0
  24. data/test/snapshots/def_parameters/expected-error.txt +0 -0
  25. data/test/snapshots/def_parameters/expected-output.txt +21 -0
  26. data/test/snapshots/def_parameters/source.lt3 +44 -0
  27. data/test/snapshots/functions_reflection/expected-error.txt +0 -0
  28. data/test/snapshots/functions_reflection/expected-output.txt +27 -0
  29. data/test/snapshots/functions_reflection/source.lt3 +5 -0
  30. data/test/snapshots/multiple_functions/expected-error.txt +0 -0
  31. data/test/snapshots/multiple_functions/expected-output.txt +5 -0
  32. data/test/snapshots/multiple_functions/source.lt3 +16 -0
  33. data/test/snapshots/nested_includes/expected-error.txt +0 -0
  34. data/test/snapshots/nested_includes/expected-output.txt +68 -0
  35. data/test/snapshots/nested_includes/level2.inc +34 -0
  36. data/test/snapshots/nested_includes/level3.inc +20 -0
  37. data/test/snapshots/nested_includes/source.lt3 +49 -0
  38. data/test/snapshots/parameter_handling/expected-error.txt +0 -0
  39. data/test/snapshots/parameter_handling/expected-output.txt +7 -0
  40. data/test/snapshots/parameter_handling/source.lt3 +10 -0
  41. data/test/snapshots/subset.txt +1 -0
  42. data/test/snapshots/system_info/expected-error.txt +0 -0
  43. data/test/snapshots/system_info/expected-output.txt +18 -0
  44. data/test/snapshots/system_info/source.lt3 +16 -0
  45. data/test/unit/all.rb +4 -0
  46. data/test/unit/core_methods.rb +137 -0
  47. data/test/unit/formatter.rb +84 -0
  48. data/test/unit/formatter_component.rb +84 -0
  49. data/test/unit/function_registry.rb +132 -0
  50. data/test/unit/variable_manager.rb +71 -0
  51. metadata +36 -3
  52. data/lib/livetext/processor.rb +0 -88
@@ -0,0 +1,84 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../../lib/livetext'
4
+
5
+ class TestingLivetextFormatter < Minitest::Test
6
+ def setup
7
+ @live = Livetext.new
8
+ end
9
+
10
+ def test_formatter_component_initialization
11
+ # Test that FormatterComponent is properly initialized
12
+ assert(@live.formatter, "FormatterComponent should be initialized")
13
+ assert_instance_of(Livetext::FormatterComponent, @live.formatter)
14
+ end
15
+
16
+ def test_basic_formatting
17
+ # Test basic text formatting
18
+ assert_equal("<b>bold</b>", @live.formatter.format("*bold"))
19
+ assert_equal("<i>italic</i>", @live.formatter.format("_italic"))
20
+ assert_equal("<tt>code</tt>", @live.formatter.format("`code"))
21
+ assert_equal("<strike>strike</strike>", @live.formatter.format("~strike"))
22
+ end
23
+
24
+ def test_double_markers
25
+ # Test double markers
26
+ assert_equal("<b>bold</b>", @live.formatter.format("**bold"))
27
+ assert_equal("**", @live.formatter.format("**")) # standalone
28
+ assert_equal(" ** ", @live.formatter.format(" ** ")) # surrounded by spaces
29
+ end
30
+
31
+ def test_bracketed_markers
32
+ # Test bracketed markers
33
+ assert_equal("<b>content</b>", @live.formatter.format("*[content]"))
34
+ assert_equal("<i>content</i>", @live.formatter.format("_[content]"))
35
+ assert_equal("<tt>content</tt>", @live.formatter.format("`[content]"))
36
+ assert_equal("", @live.formatter.format("*[]")) # empty brackets disappear
37
+ end
38
+
39
+ def test_escaped_markers
40
+ # Test escaped markers
41
+ assert_equal("*literal", @live.formatter.format("\\*literal"))
42
+ assert_equal("_literal", @live.formatter.format("\\_literal"))
43
+ assert_equal("`literal", @live.formatter.format("\\`literal"))
44
+ assert_equal("~literal", @live.formatter.format("\\~literal"))
45
+ end
46
+
47
+ def test_format_line
48
+ # Test format_line method
49
+ assert_equal("<b>bold</b>", @live.formatter.format_line("*bold\n"))
50
+ assert_equal("", @live.formatter.format_line(nil))
51
+ assert_equal("", @live.formatter.format_line(""))
52
+ end
53
+
54
+ def test_format_multiple
55
+ # Test format_multiple method
56
+ lines = ["*bold\n", "_italic\n", "`code\n"]
57
+ expected = ["<b>bold</b>", "<i>italic</i>", "<tt>code</tt>"]
58
+ assert_equal(expected, @live.formatter.format_multiple(lines))
59
+ end
60
+
61
+ def test_convenience_methods
62
+ # Test convenience methods
63
+ assert_equal("<b>text</b>", @live.formatter.bold("text"))
64
+ assert_equal("<i>text</i>", @live.formatter.italic("text"))
65
+ assert_equal("<tt>text</tt>", @live.formatter.code("text"))
66
+ assert_equal("<strike>text</strike>", @live.formatter.strike("text"))
67
+ assert_equal("<a href='url'>text</a>", @live.formatter.link("text", "url"))
68
+ end
69
+
70
+ def test_escape_html
71
+ # Test HTML escaping
72
+ assert_equal("&lt;tag&gt;", @live.formatter.escape_html("<tag>"))
73
+ assert_equal("&amp;", @live.formatter.escape_html("&"))
74
+ assert_equal("&quot;text&quot;", @live.formatter.escape_html('"text"'))
75
+ assert_equal("&#39;text&#39;", @live.formatter.escape_html("'text'"))
76
+ end
77
+
78
+ def test_edge_cases
79
+ # Test edge cases
80
+ assert_equal("", @live.formatter.format(nil))
81
+ assert_equal("", @live.formatter.format(""))
82
+ assert_equal("plain text", @live.formatter.format("plain text"))
83
+ end
84
+ end
@@ -0,0 +1,132 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../../lib/livetext'
4
+
5
+ class TestingLivetextFunctionRegistry < Minitest::Test
6
+ def setup
7
+ @live = Livetext.new
8
+ end
9
+
10
+ def check_match(exp, actual)
11
+ if exp.is_a? Regexp
12
+ assert_match(exp, actual, "actual does not match expected")
13
+ else
14
+ assert_equal(exp, actual, "actual != expected")
15
+ end
16
+ end
17
+
18
+ def test_registry_builtin_functions
19
+ # Test that builtin functions work through the registry
20
+ src = "$$date"
21
+ exp = /^\d{4}-\d{2}-\d{2}$/
22
+ actual = @live.api.format(src)
23
+ check_match(exp, actual)
24
+ end
25
+
26
+ def test_registry_user_functions
27
+ # Test that user functions are registered and work
28
+ registry = @live.function_registry
29
+
30
+ # Register a test function
31
+ registry.register_user('testfunc', ->(param) { param.upcase }, source: :inline, filename: 'test.lt3')
32
+
33
+ # Test that it works
34
+ result = registry.call('testfunc', 'hello')
35
+ assert_equal('HELLO', result)
36
+ end
37
+
38
+ def test_registry_function_override
39
+ # Test that user functions override builtin functions
40
+ registry = @live.function_registry
41
+
42
+ # Register a user function that should override the builtin
43
+ registry.register_user('reverse', ->(param) { param.upcase }, source: :inline, filename: 'test.lt3')
44
+
45
+ # Test that user function is called, not builtin
46
+ result = registry.call('reverse', 'hello')
47
+ assert_equal('HELLO', result) # User function (uppercase), not builtin (reverse)
48
+ end
49
+
50
+ def test_registry_function_listing
51
+ # Test that we can list functions
52
+ registry = @live.function_registry
53
+ functions = registry.list_functions
54
+
55
+ # Should have builtin functions
56
+ builtin_names = functions.select { |f| f[:source] == "builtin" }.map { |f| f[:name] }
57
+ assert_includes(builtin_names, "date")
58
+ assert_includes(builtin_names, "time")
59
+ assert_includes(builtin_names, "reverse")
60
+ end
61
+
62
+ def test_registry_error_handling
63
+ # Test error handling for non-existent functions
64
+ src = "$$nonexistent[param]"
65
+ exp = /Error evaluating/
66
+ actual = @live.api.format(src)
67
+ check_match(exp, actual)
68
+ end
69
+
70
+
71
+
72
+ def test_registry_function_source_tracking
73
+ # Test that function sources are tracked correctly
74
+ registry = @live.function_registry
75
+
76
+ # Register a test function
77
+ registry.register_user('testsource', ->(param) { "test" }, source: :inline, filename: 'test.lt3')
78
+
79
+ # Check that it's listed with correct source
80
+ functions = registry.list_functions
81
+ test_func = functions.find { |f| f[:name] == 'testsource' }
82
+ assert_equal("inline (test.lt3)", test_func[:source])
83
+ end
84
+
85
+ def test_registry_function_info
86
+ # Test getting detailed function information
87
+ registry = @live.function_registry
88
+
89
+ # Test builtin function info
90
+ info = registry.get_function_info('date')
91
+ assert_equal('date', info[:name])
92
+ assert_equal('builtin', info[:source])
93
+ assert_equal('builtin', info[:filename])
94
+ assert_equal(:builtin, info[:type])
95
+
96
+ # Test non-existent function
97
+ info = registry.get_function_info('nonexistent')
98
+ assert_nil(info)
99
+ end
100
+
101
+ def test_registry_mixin_source_tracking
102
+ # Test that mixin functions are tracked with correct source
103
+ registry = @live.function_registry
104
+
105
+ # Register a mixin function
106
+ registry.register_user('mixinfunc', ->(param) { "mixin" }, source: :mixin, filename: 'test_mixin.rb')
107
+
108
+ # Check that it's listed with correct source
109
+ functions = registry.list_functions
110
+ mixin_func = functions.find { |f| f[:name] == 'mixinfunc' }
111
+ assert_equal("mixin (test_mixin.rb)", mixin_func[:source])
112
+ end
113
+
114
+ def test_registry_function_exists
115
+ # Test the function_exists? method
116
+ registry = @live.function_registry
117
+
118
+ assert(registry.function_exists?('date'), "Builtin function should exist")
119
+ assert(registry.function_exists?('time'), "Builtin function should exist")
120
+ refute(registry.function_exists?('nonexistent'), "Non-existent function should not exist")
121
+ end
122
+
123
+ def test_registry_backward_compatibility
124
+ # Test that old-style function calls still work
125
+ src = "$$date"
126
+ exp = /^\d{4}-\d{2}-\d{2}$/
127
+ actual = @live.api.format(src)
128
+ check_match(exp, actual)
129
+ end
130
+
131
+
132
+ end
@@ -0,0 +1,71 @@
1
+ require 'minitest/autorun'
2
+
3
+ require_relative '../../lib/livetext'
4
+
5
+ class TestingLivetextVariableManager < Minitest::Test
6
+ def setup
7
+ @live = Livetext.new
8
+ end
9
+
10
+ def test_variable_manager_initialization
11
+ # Test that VariableManager is properly initialized
12
+ assert(@live.variables, "VariableManager should be initialized")
13
+ assert_instance_of(Livetext::VariableManager, @live.variables)
14
+ end
15
+
16
+ def test_default_variables
17
+ # Test that default variables are set
18
+ assert_equal(`whoami`.chomp, @live.variables.get(:User))
19
+ assert_equal(Livetext::VERSION, @live.variables.get(:Version))
20
+ assert_equal(`hostname`.chomp, @live.variables.get(:Hostname))
21
+ assert_equal(RUBY_PLATFORM, @live.variables.get(:Platform))
22
+ assert_equal(RUBY_VERSION, @live.variables.get(:RubyVersion))
23
+ assert_equal(Livetext::VERSION, @live.variables.get(:LivetextVersion))
24
+ end
25
+
26
+ def test_date_time_variables
27
+ # Test that date/time variables are set
28
+ now = Time.now
29
+ assert_equal(now.year.to_s, @live.variables.get(:Year))
30
+ assert_equal(now.mon.to_s, @live.variables.get(:Month))
31
+ assert_equal(now.day.to_s, @live.variables.get(:Day))
32
+ assert_equal(now.hour.to_s, @live.variables.get(:Hour))
33
+ assert_equal(now.min.to_s, @live.variables.get(:Minute))
34
+ assert_equal(now.sec.to_s, @live.variables.get(:Second))
35
+ assert_equal(now.wday.to_s, @live.variables.get(:Weekday))
36
+ assert_equal(now.strftime("%U"), @live.variables.get(:Week))
37
+ end
38
+
39
+ def test_set_and_get_variables
40
+ # Test setting and getting custom variables
41
+ @live.variables.set(:test_var, "test_value")
42
+ assert_equal("test_value", @live.variables.get(:test_var))
43
+ end
44
+
45
+ def test_set_multiple_variables
46
+ # Test setting multiple variables at once
47
+ pairs = { var1: "value1", var2: "value2" }
48
+ @live.variables.set_multiple(pairs)
49
+ assert_equal("value1", @live.variables.get(:var1))
50
+ assert_equal("value2", @live.variables.get(:var2))
51
+ end
52
+
53
+ def test_variable_exists
54
+ # Test checking if variables exist
55
+ @live.variables.set(:exists_test, "value")
56
+ assert(@live.variables.exists?(:exists_test))
57
+ refute(@live.variables.exists?(:nonexistent))
58
+ end
59
+
60
+ def test_bracket_access
61
+ # Test bracket notation access
62
+ @live.variables.set(:bracket_test, "bracket_value")
63
+ assert_equal("bracket_value", @live.variables[:bracket_test])
64
+ end
65
+
66
+ def test_backward_compatibility
67
+ # Test that the old vars interface still works
68
+ assert(@live.vars, "vars method should still work")
69
+ assert_equal(`whoami`.chomp, @live.vars.get(:User))
70
+ end
71
+ 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.52
4
+ version: 0.9.55
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hal Fulton
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-08-06 00:00:00.000000000 Z
10
+ date: 2025-08-07 00:00:00.000000000 Z
11
11
  dependencies: []
12
12
  description: A smart text processor extensible in Ruby
13
13
  email: rubyhacker@gmail.com
@@ -30,6 +30,8 @@ files:
30
30
  - lib/livetext/errors.rb
31
31
  - lib/livetext/expansion.rb
32
32
  - lib/livetext/formatter.rb
33
+ - lib/livetext/formatter_component.rb
34
+ - lib/livetext/function_registry.rb
33
35
  - lib/livetext/functions.rb
34
36
  - lib/livetext/global_helpers.rb
35
37
  - lib/livetext/handler.rb
@@ -43,11 +45,11 @@ files:
43
45
  - lib/livetext/parser/string.rb
44
46
  - lib/livetext/parsing.rb
45
47
  - lib/livetext/paths.rb
46
- - lib/livetext/processor.rb
47
48
  - lib/livetext/reopen.rb
48
49
  - lib/livetext/skeleton.rb
49
50
  - lib/livetext/standard.rb
50
51
  - lib/livetext/userapi.rb
52
+ - lib/livetext/variable_manager.rb
51
53
  - lib/livetext/variables.rb
52
54
  - lib/livetext/version.rb
53
55
  - livetext.gemspec
@@ -76,13 +78,22 @@ files:
76
78
  - test/snapshots/comments_ignored_1/expected-error.txt
77
79
  - test/snapshots/comments_ignored_1/expected-output.txt
78
80
  - test/snapshots/comments_ignored_1/source.lt3
81
+ - test/snapshots/complex_body/expected-error.txt
82
+ - test/snapshots/complex_body/expected-output.txt
83
+ - test/snapshots/complex_body/source.lt3
79
84
  - test/snapshots/copy_is_raw/expected-error.txt
80
85
  - test/snapshots/copy_is_raw/expected-output.txt
81
86
  - test/snapshots/copy_is_raw/rawtext.inc
82
87
  - test/snapshots/copy_is_raw/source.lt3
88
+ - test/snapshots/debug_command/expected-error.txt
89
+ - test/snapshots/debug_command/expected-output.txt
90
+ - test/snapshots/debug_command/source.lt3
83
91
  - test/snapshots/def_method/expected-error.txt
84
92
  - test/snapshots/def_method/expected-output.txt
85
93
  - test/snapshots/def_method/source.lt3
94
+ - test/snapshots/def_parameters/expected-error.txt
95
+ - test/snapshots/def_parameters/expected-output.txt
96
+ - test/snapshots/def_parameters/source.lt3
86
97
  - test/snapshots/error_inc_line_num/expected-output.txt
87
98
  - test/snapshots/error_inc_line_num/file2.lt3
88
99
  - test/snapshots/error_inc_line_num/match-error.txt
@@ -120,6 +131,9 @@ files:
120
131
  - test/snapshots/functions/expected-error.txt
121
132
  - test/snapshots/functions/expected-output.txt
122
133
  - test/snapshots/functions/source.lt3
134
+ - test/snapshots/functions_reflection/expected-error.txt
135
+ - test/snapshots/functions_reflection/expected-output.txt
136
+ - test/snapshots/functions_reflection/source.lt3
123
137
  - test/snapshots/hello_world/expected-error.txt
124
138
  - test/snapshots/hello_world/expected-output.txt
125
139
  - test/snapshots/hello_world/source.lt3
@@ -149,6 +163,17 @@ files:
149
163
  - test/snapshots/more_functions/expected-error.txt
150
164
  - test/snapshots/more_functions/expected-output.txt
151
165
  - test/snapshots/more_functions/source.lt3
166
+ - test/snapshots/multiple_functions/expected-error.txt
167
+ - test/snapshots/multiple_functions/expected-output.txt
168
+ - test/snapshots/multiple_functions/source.lt3
169
+ - test/snapshots/nested_includes/expected-error.txt
170
+ - test/snapshots/nested_includes/expected-output.txt
171
+ - test/snapshots/nested_includes/level2.inc
172
+ - test/snapshots/nested_includes/level3.inc
173
+ - test/snapshots/nested_includes/source.lt3
174
+ - test/snapshots/parameter_handling/expected-error.txt
175
+ - test/snapshots/parameter_handling/expected-output.txt
176
+ - test/snapshots/parameter_handling/source.lt3
152
177
  - test/snapshots/predef_vars/expected-error.txt
153
178
  - test/snapshots/predef_vars/match-output.txt
154
179
  - test/snapshots/predef_vars/source.lt3
@@ -182,6 +207,9 @@ files:
182
207
  - test/snapshots/single_raw_line/expected-output.txt
183
208
  - test/snapshots/single_raw_line/source.lt3
184
209
  - test/snapshots/subset.txt
210
+ - test/snapshots/system_info/expected-error.txt
211
+ - test/snapshots/system_info/expected-output.txt
212
+ - test/snapshots/system_info/source.lt3
185
213
  - test/snapshots/table_with_heredocs/expected-error.txt
186
214
  - test/snapshots/table_with_heredocs/expected-output.txt
187
215
  - test/snapshots/table_with_heredocs/old-exp-out.txt
@@ -192,7 +220,11 @@ files:
192
220
  - test/test_escape.lt3
193
221
  - test/unit/all.rb
194
222
  - test/unit/bracketed.rb
223
+ - test/unit/core_methods.rb
195
224
  - test/unit/double.rb
225
+ - test/unit/formatter.rb
226
+ - test/unit/formatter_component.rb
227
+ - test/unit/function_registry.rb
196
228
  - test/unit/functions.rb
197
229
  - test/unit/html.rb
198
230
  - test/unit/parser.rb
@@ -204,6 +236,7 @@ files:
204
236
  - test/unit/single.rb
205
237
  - test/unit/standard.rb
206
238
  - test/unit/stringparser.rb
239
+ - test/unit/variable_manager.rb
207
240
  - test/unit/variables.rb
208
241
  homepage: https://github.com/Hal9000/livetext
209
242
  licenses:
@@ -1,88 +0,0 @@
1
-
2
- # Class Processor does the actual work of processing input.
3
-
4
- class Processor
5
-
6
- include Livetext::Standard
7
-
8
- Disallowed =
9
- %i[ __binding__ __id__ __send__ class
10
- clone display dup enum_for
11
- eql? equal? extend freeze
12
- frozen? hash inspect instance_eval
13
- instance_exec instance_of? is_a? kind_of?
14
- method methods nil? object_id
15
- pretty_inspect private_methods protected_methods public_method
16
- public_methods public_send respond_to? send
17
- singleton_class singleton_method singleton_methods taint
18
- tainted? tap to_enum to_s
19
- trust untaint untrust untrusted?
20
- define_singleton_method instance_variable_defined?
21
- instance_variable_get instance_variable_set
22
- remove_instance_variable instance_variables ]
23
-
24
- attr_reader :parent, :sources
25
-
26
- def initialize(parent, output = nil) # Processor
27
- @parent = parent || self
28
- # STDERR.puts "PARENT.api = #{parent.api.inspect}"
29
- @parent.api ||= Livetext::UserAPI.new(@parent)
30
- @nopass = false
31
- @nopara = false
32
- # Meh?
33
- @output = ::Livetext.output = (output || File.open("/dev/null", "w"))
34
- @sources = []
35
- @indentation = @parent.indentation
36
- @_mixins = []
37
- @_imports = []
38
- @html = Livetext::HTML.new(@parent.api)
39
- end
40
-
41
- def api
42
- @parent.api # FIXME Is this weird??
43
- end
44
-
45
- def html
46
- @html
47
- end
48
-
49
- def output=(io)
50
- @output = io
51
- end
52
-
53
- def error(*args)
54
- ::STDERR.puts *args
55
- end
56
-
57
- def disallowed?(name)
58
- flag = Disallowed.include?(name.to_sym)
59
- flag
60
- end
61
-
62
- def source(enum, file, line)
63
- @sources.push([enum, file, line])
64
- end
65
-
66
- def peek_nextline
67
- return nil if @sources.empty?
68
- source = @sources.last
69
- line = source[0].peek
70
- line
71
- rescue StopIteration
72
- @sources.pop
73
- nil
74
- rescue => err
75
- TTY.puts "#{__method__}: RESCUE err = #{err.inspect}"
76
- nil
77
- end
78
-
79
- def nextline
80
- return nil if @sources.empty?
81
- line = @sources.last[0].next
82
- @sources.last[2] += 1
83
- line
84
- rescue StopIteration
85
- @sources.pop
86
- nil
87
- end
88
- end