gloo 6.7.2 → 7.0
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/CLAUDE.md +1 -0
- data/docs/application.md +3 -1
- data/docs/iterators.md +39 -3
- data/docs/language_syntax.md +53 -5
- data/docs/objects.md +58 -1
- data/docs/operators.md +16 -0
- data/docs/plugins.md +9 -1
- data/docs/verbs.md +4 -2
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +20 -0
- data/lib/gloo/app/engine.rb +64 -8
- data/lib/gloo/app/log.rb +47 -6
- data/lib/gloo/convert/converter.rb +63 -2
- data/lib/gloo/convert/string_to_date.rb +14 -0
- data/lib/gloo/convert/string_to_datetime.rb +16 -0
- data/lib/gloo/convert/string_to_decimal.rb +13 -0
- data/lib/gloo/convert/string_to_integer.rb +9 -2
- data/lib/gloo/convert/string_to_time.rb +28 -0
- data/lib/gloo/core/error.rb +11 -2
- data/lib/gloo/core/event_manager.rb +7 -4
- data/lib/gloo/core/factory.rb +16 -11
- data/lib/gloo/core/here.rb +6 -1
- data/lib/gloo/core/invoker.rb +2 -3
- data/lib/gloo/core/not_found.rb +42 -0
- data/lib/gloo/core/obj.rb +28 -2
- data/lib/gloo/core/op.rb +2 -0
- data/lib/gloo/core/parser.rb +1 -1
- data/lib/gloo/core/pn.rb +33 -12
- data/lib/gloo/core/tokens.rb +38 -0
- data/lib/gloo/core/verb.rb +54 -0
- data/lib/gloo/exec/dispatch.rb +5 -6
- data/lib/gloo/exec/exec_env.rb +13 -0
- data/lib/gloo/exec/runner.rb +10 -2
- data/lib/gloo/exec/script.rb +13 -1
- data/lib/gloo/expr/expression.rb +39 -1
- data/lib/gloo/expr/op_plus.rb +3 -0
- data/lib/gloo/objs/basic/boolean.rb +16 -0
- data/lib/gloo/objs/basic/container.rb +154 -6
- data/lib/gloo/objs/basic/decimal.rb +1 -0
- data/lib/gloo/objs/basic/integer.rb +1 -0
- data/lib/gloo/objs/basic/string.rb +0 -1
- data/lib/gloo/objs/basic/string_msgs.rb +68 -32
- data/lib/gloo/objs/ctrl/each.rb +22 -3
- data/lib/gloo/objs/ctrl/each_dir.rb +20 -7
- data/lib/gloo/objs/ctrl/each_file.rb +78 -9
- data/lib/gloo/objs/ctrl/function.rb +6 -5
- data/lib/gloo/objs/dt/date.rb +28 -11
- data/lib/gloo/objs/dt/datetime.rb +72 -21
- data/lib/gloo/objs/dt/dt_tools.rb +46 -2
- data/lib/gloo/objs/dt/time.rb +28 -11
- data/lib/gloo/objs/str_utils/cipher.rb +17 -4
- data/lib/gloo/objs/str_utils/password.rb +6 -2
- data/lib/gloo/objs/system/erb.rb +9 -3
- data/lib/gloo/objs/system/file_handle.rb +57 -21
- data/lib/gloo/objs/system/system.rb +6 -2
- data/lib/gloo/objs/web/http_get.rb +12 -2
- data/lib/gloo/objs/web/http_post.rb +6 -2
- data/lib/gloo/objs/web/json.rb +38 -18
- data/lib/gloo/objs/web/uri.rb +36 -16
- data/lib/gloo/persist/file_loader.rb +72 -11
- data/lib/gloo/persist/file_saver.rb +2 -1
- data/lib/gloo/persist/indent_stack.rb +49 -33
- data/lib/gloo/persist/line_splitter.rb +40 -3
- data/lib/gloo/persist/persist_man.rb +2 -4
- data/lib/gloo/plugin/ext_manager.rb +6 -9
- data/lib/gloo/plugin/lib_manager.rb +2 -2
- data/lib/gloo/verbs/break.rb +1 -0
- data/lib/gloo/verbs/check.rb +1 -1
- data/lib/gloo/verbs/cls.rb +1 -0
- data/lib/gloo/verbs/create.rb +32 -4
- data/lib/gloo/verbs/execute.rb +1 -1
- data/lib/gloo/verbs/exists.rb +27 -15
- data/lib/gloo/verbs/files.rb +2 -1
- data/lib/gloo/verbs/help.rb +1 -0
- data/lib/gloo/verbs/if.rb +1 -1
- data/lib/gloo/verbs/invoke.rb +1 -1
- data/lib/gloo/verbs/list.rb +4 -3
- data/lib/gloo/verbs/load.rb +3 -3
- data/lib/gloo/verbs/move.rb +10 -8
- data/lib/gloo/verbs/put.rb +20 -8
- data/lib/gloo/verbs/quit.rb +1 -0
- data/lib/gloo/verbs/redirect.rb +8 -2
- data/lib/gloo/verbs/reload.rb +9 -1
- data/lib/gloo/verbs/run.rb +2 -2
- data/lib/gloo/verbs/save.rb +4 -2
- data/lib/gloo/verbs/tell.rb +1 -1
- data/lib/gloo/verbs/unless.rb +1 -1
- data/lib/gloo/verbs/unload.rb +8 -0
- data/test.gloo/ctrl/each.test.gloo +88 -0
- data/test.gloo/lang/declaration.test.gloo +27 -0
- data/test.gloo/lang/exceptions.test.gloo +60 -0
- data/test.gloo/lang/failures.test.gloo +139 -0
- data/test.gloo/lang/it.test.gloo +133 -0
- data/test.gloo/lang/naming.test.gloo +11 -0
- data/test.gloo/lang/ops.test.gloo +30 -0
- data/test.gloo/objs/can.test.gloo +101 -0
- data/test.gloo/objs/string.test.gloo +24 -0
- data/test.gloo/string/str.test.gloo +28 -0
- data/test.gloo/verbs/create.test.gloo +36 -0
- data/test.gloo/verbs/exists.test.gloo +33 -1
- data/test.gloo/verbs/unload.test.gloo +34 -0
- metadata +4 -1
data/lib/gloo/verbs/unload.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Gloo
|
|
|
16
16
|
# This will unload all loaded objects and reset the engine state.
|
|
17
17
|
#
|
|
18
18
|
def run
|
|
19
|
+
return if extra_words_err?( 'nothing was unloaded; to unload one object, use tell x to unload' )
|
|
19
20
|
return unless @engine.persist_man.maps
|
|
20
21
|
|
|
21
22
|
@engine.persist_man.unload_all
|
|
@@ -58,6 +59,13 @@ module Gloo
|
|
|
58
59
|
'save state prior to executing.',
|
|
59
60
|
:syntax => [ 'unload' ],
|
|
60
61
|
:result => 'All objects will be unloaded from the heap.',
|
|
62
|
+
:errors => [
|
|
63
|
+
'unload takes no object, so nothing was unloaded — anything ' \
|
|
64
|
+
'written after unload is an error, and nothing is unloaded. ' \
|
|
65
|
+
'To unload one object, use tell x to unload.'
|
|
66
|
+
],
|
|
67
|
+
:notes => 'unload takes no object; to unload just one object, ' \
|
|
68
|
+
'send it the unload message (tell x to unload).',
|
|
61
69
|
:examples => <<~EXAMPLES.strip
|
|
62
70
|
> unload
|
|
63
71
|
EXAMPLES
|
|
@@ -35,3 +35,91 @@ tests.ctrl.each [can] :
|
|
|
35
35
|
tell ^^.rpt to run
|
|
36
36
|
eval ^^.count = 3
|
|
37
37
|
assert "expected repeat to run 3 times"
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Each file / each dir over a nested folder tree.
|
|
41
|
+
# The tree is built under /tmp and removed at the end.
|
|
42
|
+
#
|
|
43
|
+
|
|
44
|
+
tests.ctrl.each_folder [can] :
|
|
45
|
+
|
|
46
|
+
count [int] : 0
|
|
47
|
+
|
|
48
|
+
make_tree [system] :
|
|
49
|
+
command [string] : rm -rf /tmp/gloo_each_walk && mkdir -p /tmp/gloo_each_walk/a/deep /tmp/gloo_each_walk/.obs && touch /tmp/gloo_each_walk/top.md /tmp/gloo_each_walk/notes.txt /tmp/gloo_each_walk/a/x.md /tmp/gloo_each_walk/a/deep/Y.MD /tmp/gloo_each_walk/.obs/o.md
|
|
50
|
+
get_output [bool] : false
|
|
51
|
+
remove_tree [system] :
|
|
52
|
+
command [string] : rm -rf /tmp/gloo_each_walk
|
|
53
|
+
get_output [bool] : false
|
|
54
|
+
|
|
55
|
+
files [each] :
|
|
56
|
+
file [file] :
|
|
57
|
+
in [file] : /tmp/gloo_each_walk
|
|
58
|
+
ext [string] : md
|
|
59
|
+
recursive [bool] : true
|
|
60
|
+
do [script] :
|
|
61
|
+
eval ^^.count + 1
|
|
62
|
+
put it into ^^.count
|
|
63
|
+
|
|
64
|
+
wild [each] :
|
|
65
|
+
file [file] :
|
|
66
|
+
in [file] : /tmp/gloo_each_walk/**/
|
|
67
|
+
ext [string] : md
|
|
68
|
+
do [script] :
|
|
69
|
+
eval ^^.count + 1
|
|
70
|
+
put it into ^^.count
|
|
71
|
+
|
|
72
|
+
top [each] :
|
|
73
|
+
file [file] :
|
|
74
|
+
in [file] : /tmp/gloo_each_walk
|
|
75
|
+
do [script] :
|
|
76
|
+
eval ^^.count + 1
|
|
77
|
+
put it into ^^.count
|
|
78
|
+
|
|
79
|
+
dirs [each] :
|
|
80
|
+
dir [file] :
|
|
81
|
+
in [file] : /tmp/gloo_each_walk
|
|
82
|
+
recursive [bool] : true
|
|
83
|
+
do [script] :
|
|
84
|
+
eval ^^.count + 1
|
|
85
|
+
put it into ^^.count
|
|
86
|
+
|
|
87
|
+
recursive_each_file [test] :
|
|
88
|
+
description [string] : Recursive each file finds nested files, ignoring ext case and hidden folders.
|
|
89
|
+
on_test [script] :
|
|
90
|
+
tell ^^.make_tree to run
|
|
91
|
+
put 0 into ^^.count
|
|
92
|
+
tell ^^.files to run
|
|
93
|
+
eval ^^.count = 3
|
|
94
|
+
assert "expected top.md, a/x.md and a/deep/Y.MD"
|
|
95
|
+
tell ^^.remove_tree to run
|
|
96
|
+
|
|
97
|
+
wildcard_in_folder [test] :
|
|
98
|
+
description [string] : A ** wildcard in the folder walks the tree too.
|
|
99
|
+
on_test [script] :
|
|
100
|
+
tell ^^.make_tree to run
|
|
101
|
+
put 0 into ^^.count
|
|
102
|
+
tell ^^.wild to run
|
|
103
|
+
eval ^^.count = 3
|
|
104
|
+
assert "expected the same 3 files as recursive"
|
|
105
|
+
tell ^^.remove_tree to run
|
|
106
|
+
|
|
107
|
+
files_only_by_default [test] :
|
|
108
|
+
description [string] : Each file without ext lists files, not folders.
|
|
109
|
+
on_test [script] :
|
|
110
|
+
tell ^^.make_tree to run
|
|
111
|
+
put 0 into ^^.count
|
|
112
|
+
tell ^^.top to run
|
|
113
|
+
eval ^^.count = 2
|
|
114
|
+
assert "expected top.md and notes.txt, but not the folder a"
|
|
115
|
+
tell ^^.remove_tree to run
|
|
116
|
+
|
|
117
|
+
recursive_each_dir [test] :
|
|
118
|
+
description [string] : Recursive each dir lists every folder in the tree, skipping hidden ones.
|
|
119
|
+
on_test [script] :
|
|
120
|
+
tell ^^.make_tree to run
|
|
121
|
+
put 0 into ^^.count
|
|
122
|
+
tell ^^.dirs to run
|
|
123
|
+
eval ^^.count = 2
|
|
124
|
+
assert "expected a and a/deep"
|
|
125
|
+
tell ^^.remove_tree to run
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Declaration syntax tests
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
tests.lang.declaration [can] :
|
|
6
|
+
|
|
7
|
+
# Deliberately missing the ']' after its type: loading reports a
|
|
8
|
+
# syntax error and reads the type as 'int' anyway.
|
|
9
|
+
unclosed [int : 7
|
|
10
|
+
|
|
11
|
+
closed_then_colon [int]: 8
|
|
12
|
+
|
|
13
|
+
type_missing_its_closing_bracket [test] :
|
|
14
|
+
description [string] : a type missing its ']' is still read as that type, with its value.
|
|
15
|
+
on_test [script] :
|
|
16
|
+
eval ^^.unclosed = 7
|
|
17
|
+
assert "expected the value 7"
|
|
18
|
+
|
|
19
|
+
put '12' into ^^.unclosed
|
|
20
|
+
eval ^^.unclosed = 12
|
|
21
|
+
assert "expected it to still be an integer"
|
|
22
|
+
|
|
23
|
+
closed_type_followed_by_the_colon [test] :
|
|
24
|
+
description [string] : a ']' followed directly by ':' is a normal declaration.
|
|
25
|
+
on_test [script] :
|
|
26
|
+
eval ^^.closed_then_colon = 8
|
|
27
|
+
assert "expected the value 8"
|
|
@@ -20,6 +20,8 @@ tests.lang.exceptions [can] :
|
|
|
20
20
|
error_data [can] :
|
|
21
21
|
message [string] :
|
|
22
22
|
backtrace [string] :
|
|
23
|
+
kind [string] :
|
|
24
|
+
location [string] :
|
|
23
25
|
|
|
24
26
|
confirm_throw_fires_on_exception [test] :
|
|
25
27
|
description [string] : Confirm throw fires on_exception and populates exception_data, but does not fire on_error.
|
|
@@ -59,3 +61,61 @@ tests.lang.exceptions [can] :
|
|
|
59
61
|
|
|
60
62
|
eval ^^.exc_fired = false
|
|
61
63
|
assert "expected on_exception to NOT have fired for a gloo-level error"
|
|
64
|
+
|
|
65
|
+
confirm_missing_value_fires_on_error [test] :
|
|
66
|
+
description [string] : Using a missing object as a value is a runtime error, but asking whether it exists is not.
|
|
67
|
+
on_test [script] :
|
|
68
|
+
put false into ^^.err_fired
|
|
69
|
+
exists? instance ^^.no_such_value
|
|
70
|
+
eval ^^.err_fired = false
|
|
71
|
+
assert "expected exists? on a missing object to not fire on_error"
|
|
72
|
+
|
|
73
|
+
show ^^.no_such_value
|
|
74
|
+
eval ^^.err_fired = true
|
|
75
|
+
assert "expected using a missing object as a value to fire on_error"
|
|
76
|
+
|
|
77
|
+
eval ^^.error_data.message = "Object '^^.no_such_value' was not found."
|
|
78
|
+
assert "expected error_data.message to name the missing object"
|
|
79
|
+
|
|
80
|
+
kept [string] : keep me
|
|
81
|
+
|
|
82
|
+
confirm_put_of_missing_value_keeps_target [test] :
|
|
83
|
+
description [string] : A put whose value uses a missing object leaves the target unchanged.
|
|
84
|
+
on_test [script] :
|
|
85
|
+
put 'keep me' into ^^.kept
|
|
86
|
+
put ^^.no_such_value into ^^.kept
|
|
87
|
+
eval ^^.kept = 'keep me'
|
|
88
|
+
assert "expected the target to keep its value"
|
|
89
|
+
|
|
90
|
+
confirm_error_kind [test] :
|
|
91
|
+
description [string] : error_data.kind says whether the command couldn't be understood (syntax) or couldn't be done (runtime).
|
|
92
|
+
on_test [script] :
|
|
93
|
+
nosuchverb
|
|
94
|
+
eval ^^.error_data.kind = 'syntax'
|
|
95
|
+
assert "expected an unknown verb to be a syntax error"
|
|
96
|
+
|
|
97
|
+
show ^^.no_such_value
|
|
98
|
+
eval ^^.error_data.kind = 'runtime'
|
|
99
|
+
assert "expected a missing object to be a runtime error"
|
|
100
|
+
|
|
101
|
+
confirm_error_location [test] :
|
|
102
|
+
description [string] : error_data.location names the script and line that failed.
|
|
103
|
+
on_test [script] :
|
|
104
|
+
show 1
|
|
105
|
+
show ^^.no_such_value
|
|
106
|
+
eval ^^.error_data.location = 'tests.lang.exceptions.confirm_error_location.on_test, line 2'
|
|
107
|
+
assert "expected the location of the failed line"
|
|
108
|
+
|
|
109
|
+
num [int] : 5
|
|
110
|
+
|
|
111
|
+
confirm_bad_value_is_a_warning [test] :
|
|
112
|
+
description [string] : A value that isn't really of its type is a warning, not an error; the best guess is used.
|
|
113
|
+
on_test [script] :
|
|
114
|
+
put false into ^^.err_fired
|
|
115
|
+
put 'x' into ^^.num
|
|
116
|
+
eval ^^.err_fired = false
|
|
117
|
+
assert "expected a bad value to not fire on_error"
|
|
118
|
+
|
|
119
|
+
eval ^^.num = 0
|
|
120
|
+
assert "expected the best guess, 0"
|
|
121
|
+
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Predictable failures tests
|
|
3
|
+
#
|
|
4
|
+
# A failure a script can cause with ordinary bad input or the outside
|
|
5
|
+
# world (invalid JSON, a template with a mistake, a value that isn't a
|
|
6
|
+
# date) is a gloo runtime error: it fires on_error (not on_exception),
|
|
7
|
+
# reads "Could not ...", and a command that would set it sets it false.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
tests.lang.failures [can] :
|
|
11
|
+
|
|
12
|
+
err_fired [bool] : false
|
|
13
|
+
exc_fired [bool] : false
|
|
14
|
+
|
|
15
|
+
on_error [script] :
|
|
16
|
+
put true into ^.err_fired
|
|
17
|
+
|
|
18
|
+
on_exception [script] :
|
|
19
|
+
put true into ^.exc_fired
|
|
20
|
+
|
|
21
|
+
error_data [can] :
|
|
22
|
+
message [string] :
|
|
23
|
+
backtrace [string] :
|
|
24
|
+
kind [string] :
|
|
25
|
+
location [string] :
|
|
26
|
+
|
|
27
|
+
j [json] : {bad
|
|
28
|
+
e [erb] :
|
|
29
|
+
template [text] : <%= 1 + %>
|
|
30
|
+
params [can] :
|
|
31
|
+
result [text] :
|
|
32
|
+
d [date] :
|
|
33
|
+
msg [string] :
|
|
34
|
+
|
|
35
|
+
invalid_json_is_an_error [test] :
|
|
36
|
+
description [string] : Pretty-printing invalid JSON fires on_error, not on_exception, and it is false.
|
|
37
|
+
on_test [script] :
|
|
38
|
+
put false into ^^.err_fired
|
|
39
|
+
put false into ^^.exc_fired
|
|
40
|
+
tell ^^.j to pretty
|
|
41
|
+
eval it = false
|
|
42
|
+
assert "expected it to be false"
|
|
43
|
+
|
|
44
|
+
eval ^^.err_fired = true
|
|
45
|
+
assert "expected on_error to fire"
|
|
46
|
+
eval ^^.exc_fired = false
|
|
47
|
+
assert "expected on_exception not to fire"
|
|
48
|
+
|
|
49
|
+
put ^^.error_data.message into ^^.msg
|
|
50
|
+
check ^^.msg for starts_with? ( 'Could not parse the JSON in tests.lang.failures.j:' )
|
|
51
|
+
assert "expected the error to say what couldn't be done"
|
|
52
|
+
|
|
53
|
+
template_syntax_error_is_an_error [test] :
|
|
54
|
+
description [string] : An erb template with a syntax error is a gloo error, and gloo keeps running.
|
|
55
|
+
on_test [script] :
|
|
56
|
+
put false into ^^.err_fired
|
|
57
|
+
put false into ^^.exc_fired
|
|
58
|
+
run ^^.e
|
|
59
|
+
eval it = false
|
|
60
|
+
assert "expected it to be false"
|
|
61
|
+
|
|
62
|
+
eval ^^.err_fired = true
|
|
63
|
+
assert "expected on_error to fire"
|
|
64
|
+
eval ^^.exc_fired = false
|
|
65
|
+
assert "expected on_exception not to fire"
|
|
66
|
+
|
|
67
|
+
not_a_date_is_an_error [test] :
|
|
68
|
+
description [string] : A date message on a value that isn't a date fires on_error, and it is false.
|
|
69
|
+
on_test [script] :
|
|
70
|
+
put '' into ^^.d
|
|
71
|
+
put false into ^^.err_fired
|
|
72
|
+
put false into ^^.exc_fired
|
|
73
|
+
tell ^^.d to mm
|
|
74
|
+
eval it = false
|
|
75
|
+
assert "expected it to be false"
|
|
76
|
+
|
|
77
|
+
eval ^^.err_fired = true
|
|
78
|
+
assert "expected on_error to fire"
|
|
79
|
+
eval ^^.exc_fired = false
|
|
80
|
+
assert "expected on_exception not to fire"
|
|
81
|
+
|
|
82
|
+
unknown_unit_is_an_error [test] :
|
|
83
|
+
description [string] : Adding an amount with an unknown unit fires on_error, and it is false.
|
|
84
|
+
on_test [script] :
|
|
85
|
+
put '2026-01-01' into ^^.d
|
|
86
|
+
put false into ^^.err_fired
|
|
87
|
+
tell ^^.d to add ( '3 dayz' )
|
|
88
|
+
eval it = false
|
|
89
|
+
assert "expected it to be false"
|
|
90
|
+
|
|
91
|
+
eval ^^.err_fired = true
|
|
92
|
+
assert "expected on_error to fire"
|
|
93
|
+
|
|
94
|
+
eval ^^.error_data.message = "'3 dayz' is not an amount like '1 day' or '3 months'."
|
|
95
|
+
assert "expected the error to show what an amount looks like"
|
|
96
|
+
|
|
97
|
+
#
|
|
98
|
+
# Chronic phrases are still dates: the checks above only catch a value
|
|
99
|
+
# that couldn't be read as one.
|
|
100
|
+
#
|
|
101
|
+
|
|
102
|
+
tests.lang.chronic_phrases [can] :
|
|
103
|
+
|
|
104
|
+
d [date] :
|
|
105
|
+
now [date] :
|
|
106
|
+
dt [datetime] :
|
|
107
|
+
a [string] :
|
|
108
|
+
b [string] :
|
|
109
|
+
|
|
110
|
+
today_is_a_date [test] :
|
|
111
|
+
description [string] : put 'today' into a date gives today's date.
|
|
112
|
+
on_test [script] :
|
|
113
|
+
put 'today' into ^^.d
|
|
114
|
+
tell ^^.now to now
|
|
115
|
+
tell ^^.d to format ( '%Y-%m-%d' )
|
|
116
|
+
put it into ^^.a
|
|
117
|
+
tell ^^.now to format ( '%Y-%m-%d' )
|
|
118
|
+
put it into ^^.b
|
|
119
|
+
eval ^^.a = ^^.b
|
|
120
|
+
assert "expected 'today' to be today's date"
|
|
121
|
+
|
|
122
|
+
add_to_a_phrase [test] :
|
|
123
|
+
description [string] : A date set from a phrase can be added to.
|
|
124
|
+
on_test [script] :
|
|
125
|
+
put '2026-01-01' into ^^.d
|
|
126
|
+
tell ^^.d to add ( '1 week' )
|
|
127
|
+
eval it = '2026.01.08'
|
|
128
|
+
assert "expected a week later"
|
|
129
|
+
|
|
130
|
+
tomorrow_is_in_the_future [test] :
|
|
131
|
+
description [string] : 'tomorrow at 3pm' in a datetime is tomorrow, and in the future.
|
|
132
|
+
on_test [script] :
|
|
133
|
+
put 'tomorrow at 3pm' into ^^.dt
|
|
134
|
+
tell ^^.dt to is_tomorrow
|
|
135
|
+
assert "expected it to be tomorrow"
|
|
136
|
+
tell ^^.dt to is_future
|
|
137
|
+
assert "expected it to be in the future"
|
|
138
|
+
tell ^^.dt to is_past
|
|
139
|
+
refute "expected it not to be in the past"
|
data/test.gloo/lang/it.test.gloo
CHANGED
|
@@ -37,3 +37,136 @@ tests.lang.it [can] :
|
|
|
37
37
|
list ^^.n
|
|
38
38
|
eval it = 42
|
|
39
39
|
assert "expected it to survive statements that don't produce a result"
|
|
40
|
+
|
|
41
|
+
#
|
|
42
|
+
# it is a read-only result value, not an object: it can be read, but
|
|
43
|
+
# nothing can target it.
|
|
44
|
+
#
|
|
45
|
+
|
|
46
|
+
tests.lang.it_read_only [can] :
|
|
47
|
+
|
|
48
|
+
err_fired [bool] : false
|
|
49
|
+
s [string] :
|
|
50
|
+
|
|
51
|
+
on_error [script] :
|
|
52
|
+
put true into ^.err_fired
|
|
53
|
+
|
|
54
|
+
error_data [can] :
|
|
55
|
+
message [string] :
|
|
56
|
+
backtrace [string] :
|
|
57
|
+
kind [string] :
|
|
58
|
+
location [string] :
|
|
59
|
+
|
|
60
|
+
message_to_it_is_an_error [test] :
|
|
61
|
+
description [string] : Sending it a message reports an error that says what to do instead.
|
|
62
|
+
on_test [script] :
|
|
63
|
+
put false into ^^.err_fired
|
|
64
|
+
eval 'abc'
|
|
65
|
+
check it for starts_with? ( 'a' )
|
|
66
|
+
eval ^^.err_fired = true
|
|
67
|
+
assert "expected check it for ... to be an error"
|
|
68
|
+
|
|
69
|
+
eval ^^.error_data.message = "it isn't an object and can't be sent messages; put it into an object first (put it into x)."
|
|
70
|
+
assert "expected the error to say to put it into an object first"
|
|
71
|
+
|
|
72
|
+
put_into_it_is_an_error [test] :
|
|
73
|
+
description [string] : Putting a value into it is an error.
|
|
74
|
+
on_test [script] :
|
|
75
|
+
put false into ^^.err_fired
|
|
76
|
+
put 'x' into it
|
|
77
|
+
eval ^^.err_fired = true
|
|
78
|
+
assert "expected put into it to be an error"
|
|
79
|
+
|
|
80
|
+
put_it_into_an_object_works [test] :
|
|
81
|
+
description [string] : The workaround: put it into an object, then send that object the message.
|
|
82
|
+
on_test [script] :
|
|
83
|
+
put false into ^^.err_fired
|
|
84
|
+
eval 'abc'
|
|
85
|
+
put it into ^^.s
|
|
86
|
+
check ^^.s for starts_with? ( 'a' )
|
|
87
|
+
assert "expected the message to work on the object"
|
|
88
|
+
|
|
89
|
+
eval ^^.err_fired = false
|
|
90
|
+
assert "expected no error"
|
|
91
|
+
|
|
92
|
+
#
|
|
93
|
+
# The it rule: a command that works out a value puts it in it; when
|
|
94
|
+
# something goes wrong, it is false.
|
|
95
|
+
#
|
|
96
|
+
|
|
97
|
+
tests.lang.it_rule [can] :
|
|
98
|
+
|
|
99
|
+
err_fired [bool] : false
|
|
100
|
+
s [string] :
|
|
101
|
+
kept [string] : keep me
|
|
102
|
+
|
|
103
|
+
on_error [script] :
|
|
104
|
+
put true into ^.err_fired
|
|
105
|
+
|
|
106
|
+
failed_put_sets_it_false [test] :
|
|
107
|
+
description [string] : A put whose value can't be worked out sets it to false and keeps the target.
|
|
108
|
+
on_test [script] :
|
|
109
|
+
put false into ^^.err_fired
|
|
110
|
+
eval 'before'
|
|
111
|
+
put ^^.no_such_value into ^^.kept
|
|
112
|
+
eval it = false
|
|
113
|
+
assert "expected it to be false after a failed put"
|
|
114
|
+
|
|
115
|
+
eval ^^.kept = 'keep me'
|
|
116
|
+
assert "expected the target to keep its value"
|
|
117
|
+
|
|
118
|
+
eval ^^.err_fired = true
|
|
119
|
+
assert "expected the failed put to be an error"
|
|
120
|
+
|
|
121
|
+
put_into_missing_object_sets_it_false [test] :
|
|
122
|
+
description [string] : A put into an object that doesn't exist sets it to false.
|
|
123
|
+
on_test [script] :
|
|
124
|
+
eval 'before'
|
|
125
|
+
put 'x' into ^^.no_such_target
|
|
126
|
+
eval it = false
|
|
127
|
+
assert "expected it to be false"
|
|
128
|
+
|
|
129
|
+
value_messages_set_it [test] :
|
|
130
|
+
description [string] : A message that changes its own value also puts the new value in it.
|
|
131
|
+
on_test [script] :
|
|
132
|
+
put ' padded ' into ^^.s
|
|
133
|
+
tell ^^.s to trim
|
|
134
|
+
eval it = 'padded'
|
|
135
|
+
assert "expected trim to put the trimmed string in it"
|
|
136
|
+
|
|
137
|
+
put 'a b' into ^^.s
|
|
138
|
+
tell ^^.s to format_for_html
|
|
139
|
+
eval it = 'a b'
|
|
140
|
+
assert "expected format_for_html to put the result in it"
|
|
141
|
+
|
|
142
|
+
failed_function_sets_it_false [test] :
|
|
143
|
+
description [string] : A function whose on_invoke fails sets it to false.
|
|
144
|
+
on_test [script] :
|
|
145
|
+
eval 'before'
|
|
146
|
+
invoke ^^.failing
|
|
147
|
+
eval it = false
|
|
148
|
+
assert "expected it to be false after a failed invoke"
|
|
149
|
+
|
|
150
|
+
failing [ƒ] :
|
|
151
|
+
params [can] :
|
|
152
|
+
on_invoke [script] :
|
|
153
|
+
put 1 into no.such.object
|
|
154
|
+
result [int] :
|
|
155
|
+
|
|
156
|
+
message_to_missing_object_sets_it_false [test] :
|
|
157
|
+
description [string] : A message that can't be delivered leaves no stale value in it.
|
|
158
|
+
on_test [script] :
|
|
159
|
+
eval 'stale'
|
|
160
|
+
tell ^^.no_such_obj to get_parent
|
|
161
|
+
eval it = false
|
|
162
|
+
assert "expected it to be false after a message to a missing object"
|
|
163
|
+
|
|
164
|
+
eval 'stale'
|
|
165
|
+
tell ^^.s to no_such_msg
|
|
166
|
+
eval it = false
|
|
167
|
+
assert "expected it to be false after a message the object doesn't take"
|
|
168
|
+
|
|
169
|
+
eval 'stale'
|
|
170
|
+
run ^^.no_such_obj
|
|
171
|
+
eval it = false
|
|
172
|
+
assert "expected it to be false after running a missing object"
|
|
@@ -14,6 +14,10 @@ tests.lang.naming [can] :
|
|
|
14
14
|
string [string] : a type keyword as a name
|
|
15
15
|
put [string] : a verb keyword as a name
|
|
16
16
|
|
|
17
|
+
# Deliberately has no name: loading reports a syntax error and
|
|
18
|
+
# creates it as unnamed_1.
|
|
19
|
+
[string] : no name given
|
|
20
|
+
|
|
17
21
|
first_of_a_duplicate_name_wins [test] :
|
|
18
22
|
description [string] : when two siblings share a name, the path always reaches the first.
|
|
19
23
|
on_test [script] :
|
|
@@ -40,3 +44,10 @@ tests.lang.naming [can] :
|
|
|
40
44
|
|
|
41
45
|
eval ^^.put = 'a verb keyword as a name'
|
|
42
46
|
assert "expected a verb keyword to be usable as a name"
|
|
47
|
+
|
|
48
|
+
declaration_with_no_name_gets_a_placeholder [test] :
|
|
49
|
+
description [string] : a declaration with no name loads under the placeholder name unnamed_1.
|
|
50
|
+
on_test [script] :
|
|
51
|
+
eval ^^.unnamed_1 = 'no name given'
|
|
52
|
+
assert "expected the nameless object to be reached as unnamed_1"
|
|
53
|
+
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
tests.lang.ops [can] :
|
|
6
6
|
|
|
7
|
+
first [string] : Ada
|
|
8
|
+
last [string] : Lovelace
|
|
9
|
+
full [string] :
|
|
10
|
+
|
|
7
11
|
confirm_eq_ops [test] :
|
|
8
12
|
description [string] : Confirm equality operations.
|
|
9
13
|
on_test [script] :
|
|
@@ -75,3 +79,29 @@ tests.lang.ops [can] :
|
|
|
75
79
|
|
|
76
80
|
eval 1 <= 1
|
|
77
81
|
assert "expected 1 <= 1"
|
|
82
|
+
|
|
83
|
+
confirm_and_joins [test] :
|
|
84
|
+
description [string] : and joins values, the same as +.
|
|
85
|
+
on_test [script] :
|
|
86
|
+
put ^^.first and ' ' and ^^.last into ^^.full
|
|
87
|
+
eval ^^.full = 'Ada Lovelace'
|
|
88
|
+
assert "expected first and ' ' and last to be 'Ada Lovelace'"
|
|
89
|
+
|
|
90
|
+
eval 2 and 3 = 5
|
|
91
|
+
assert "expected 2 and 3 to be 5"
|
|
92
|
+
|
|
93
|
+
confirm_join_without_operator [test] :
|
|
94
|
+
description [string] : Values side by side with no operator between them are joined.
|
|
95
|
+
on_test [script] :
|
|
96
|
+
put 'hello ' 'world' into ^^.full
|
|
97
|
+
eval ^^.full = 'hello world'
|
|
98
|
+
assert "expected 'hello ' 'world' to be 'hello world'"
|
|
99
|
+
|
|
100
|
+
put ^^.first ' ' ^^.last into ^^.full
|
|
101
|
+
eval ^^.full = 'Ada Lovelace'
|
|
102
|
+
assert "expected first ' ' last to be 'Ada Lovelace'"
|
|
103
|
+
|
|
104
|
+
put 'a' 'b' + 'c' and 'd' into ^^.full
|
|
105
|
+
eval ^^.full = 'abcd'
|
|
106
|
+
assert "expected no operator, + and and to mix"
|
|
107
|
+
|
|
@@ -43,3 +43,104 @@ tests.objs.can [can] :
|
|
|
43
43
|
eval it = 0
|
|
44
44
|
assert "expected it to be 0"
|
|
45
45
|
|
|
46
|
+
|
|
47
|
+
#
|
|
48
|
+
# Child value and path messages.
|
|
49
|
+
#
|
|
50
|
+
|
|
51
|
+
tests.objs.can_child [can] :
|
|
52
|
+
|
|
53
|
+
list [can] :
|
|
54
|
+
x [string] : ex
|
|
55
|
+
y [integer] : 7
|
|
56
|
+
book [can] :
|
|
57
|
+
title [string] : Walden
|
|
58
|
+
|
|
59
|
+
empty [can] :
|
|
60
|
+
path [string] :
|
|
61
|
+
ptr [alias] :
|
|
62
|
+
slot [alias] :
|
|
63
|
+
idx [integer] : 0
|
|
64
|
+
n [integer] : 0
|
|
65
|
+
found [integer] : 0
|
|
66
|
+
picked [can] :
|
|
67
|
+
|
|
68
|
+
# Pick distinct random children: a random index, skipped if already used.
|
|
69
|
+
pick_distinct [repeat] :
|
|
70
|
+
times [integer] : 200
|
|
71
|
+
index [integer] : 0
|
|
72
|
+
do [script] :
|
|
73
|
+
tell tests.objs.can_child.idx to randomize ( tests.objs.can_child.n )
|
|
74
|
+
check tests.objs.can_child.picked for child_exists ( tests.objs.can_child.idx )
|
|
75
|
+
unless it do tell tests.objs.can_child.mark to run
|
|
76
|
+
|
|
77
|
+
mark [script] :
|
|
78
|
+
put 'tests.objs.can_child.picked.' + tests.objs.can_child.idx into tests.objs.can_child.slot*
|
|
79
|
+
create tests.objs.can_child.slot* as integer
|
|
80
|
+
put tests.objs.can_child.found + 1 into tests.objs.can_child.found
|
|
81
|
+
|
|
82
|
+
value_at [test] :
|
|
83
|
+
description [string] : child_value_at gets a simple child's value by 0-based index.
|
|
84
|
+
on_test [script] :
|
|
85
|
+
tell ^^.list to child_value_at ( 0 )
|
|
86
|
+
eval it = 'ex'
|
|
87
|
+
assert "expected index 0 to be the first child"
|
|
88
|
+
|
|
89
|
+
tell ^^.list to child_value_at ( 1 )
|
|
90
|
+
eval it = 7
|
|
91
|
+
assert "expected index 1 to be the second child"
|
|
92
|
+
|
|
93
|
+
value_at_container_child [test] :
|
|
94
|
+
description [string] : child_value_at on a container child is an error, and it is false.
|
|
95
|
+
on_test [script] :
|
|
96
|
+
tell ^^.list to child_value_at ( 2 )
|
|
97
|
+
eval it = false
|
|
98
|
+
assert "expected false for a container child"
|
|
99
|
+
|
|
100
|
+
path_at_alias [test] :
|
|
101
|
+
description [string] : child_path_at into an alias reaches a container child's fields.
|
|
102
|
+
on_test [script] :
|
|
103
|
+
tell ^^.list to child_path_at ( 2 )
|
|
104
|
+
put it into ^^.path
|
|
105
|
+
eval ^^.path = 'tests.objs.can_child.list.book'
|
|
106
|
+
assert "expected the path from root"
|
|
107
|
+
|
|
108
|
+
put ^^.path into ^^.ptr*
|
|
109
|
+
eval ^^.ptr.title = 'Walden'
|
|
110
|
+
assert "expected to read the field through the alias"
|
|
111
|
+
|
|
112
|
+
out_of_range [test] :
|
|
113
|
+
description [string] : An out-of-range index is an error, and it is false.
|
|
114
|
+
on_test [script] :
|
|
115
|
+
tell ^^.list to child_path_at ( 3 )
|
|
116
|
+
eval it = false
|
|
117
|
+
assert "expected false for index 3 of 3 children"
|
|
118
|
+
|
|
119
|
+
random_path [test] :
|
|
120
|
+
description [string] : random_child_path gives the path of one of the children.
|
|
121
|
+
on_test [script] :
|
|
122
|
+
tell ^^.list to random_child_path
|
|
123
|
+
put it into ^^.path
|
|
124
|
+
check ^^.path for starts_with? ( 'tests.objs.can_child.list.' )
|
|
125
|
+
assert "expected a child's path"
|
|
126
|
+
|
|
127
|
+
random_empty [test] :
|
|
128
|
+
description [string] : random_child_value on an empty container is an error, and it is false.
|
|
129
|
+
on_test [script] :
|
|
130
|
+
tell ^^.empty to random_child_value
|
|
131
|
+
eval it = false
|
|
132
|
+
assert "expected false for an empty container"
|
|
133
|
+
|
|
134
|
+
distinct_random_picks [test] :
|
|
135
|
+
description [string] : A random index plus a picked container gives distinct picks.
|
|
136
|
+
on_test [script] :
|
|
137
|
+
tell ^^.list to count
|
|
138
|
+
put it into ^^.n
|
|
139
|
+
tell ^^.pick_distinct to run
|
|
140
|
+
|
|
141
|
+
tell ^^.picked to count
|
|
142
|
+
eval it = 3
|
|
143
|
+
assert "expected every child picked exactly once"
|
|
144
|
+
|
|
145
|
+
eval ^^.found = 3
|
|
146
|
+
assert "expected no index to be picked twice"
|
|
@@ -52,6 +52,30 @@ tests.objs.string [can] :
|
|
|
52
52
|
eval ^^.s = 'one two three'
|
|
53
53
|
assert "expected split to leave the string unchanged"
|
|
54
54
|
|
|
55
|
+
index_of [test] :
|
|
56
|
+
description [string] : Find the position of a substring
|
|
57
|
+
on_test [script] :
|
|
58
|
+
put 'one two three' into ^^.s
|
|
59
|
+
tell ^^.s to index_of ('two')
|
|
60
|
+
eval it = 4
|
|
61
|
+
assert "expected index_of ('two') to equal 4"
|
|
62
|
+
tell ^^.s to index_of ('four')
|
|
63
|
+
eval it = -1
|
|
64
|
+
assert "expected index_of a missing needle to equal -1"
|
|
65
|
+
eval ^^.s = 'one two three'
|
|
66
|
+
assert "expected index_of to leave the string unchanged"
|
|
67
|
+
|
|
68
|
+
index_of_from [test] :
|
|
69
|
+
description [string] : Find a substring at or after a start position
|
|
70
|
+
on_test [script] :
|
|
71
|
+
put '![[123.png]] and ]]' into ^^.s
|
|
72
|
+
tell ^^.s to index_of (']]' 3)
|
|
73
|
+
eval it = 10
|
|
74
|
+
assert "expected index_of (']]' 3) to equal 10"
|
|
75
|
+
tell ^^.s to index_of (']]' 11)
|
|
76
|
+
eval it = 17
|
|
77
|
+
assert "expected index_of (']]' 11) to equal 17"
|
|
78
|
+
|
|
55
79
|
splitl [test] :
|
|
56
80
|
description [string] : Get the substring to the left of an index
|
|
57
81
|
on_test [script] :
|