gloo 7.0.4 → 7.1.1
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/docs/application.md +16 -2
- data/docs/development.md +1 -1
- data/docs/iterators.md +24 -0
- data/docs/language_objects.md +34 -0
- data/docs/language_syntax.md +26 -1
- data/docs/objects.md +1 -1
- data/docs/plugins.md +10 -4
- data/docs/testing.md +206 -0
- data/docs/verbs.md +7 -0
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +18 -2
- data/lib/gloo/app/engine.rb +19 -9
- data/lib/gloo/app/log.rb +2 -2
- data/lib/gloo/app/settings.rb +20 -3
- data/lib/gloo/core/gloo_system.rb +7 -0
- data/lib/gloo/core/invoker.rb +20 -7
- data/lib/gloo/core/obj.rb +22 -1
- data/lib/gloo/core/tokens.rb +20 -0
- data/lib/gloo/core/verb.rb +31 -0
- data/lib/gloo/exec/exec_env.rb +6 -3
- data/lib/gloo/exec/script.rb +7 -0
- data/lib/gloo/objs/basic/script.rb +9 -0
- data/lib/gloo/objs/basic/string_msgs.rb +6 -7
- data/lib/gloo/objs/basic/untyped.rb +2 -1
- data/lib/gloo/objs/ctrl/each.rb +4 -2
- data/lib/gloo/objs/ctrl/each_child.rb +1 -1
- data/lib/gloo/objs/ctrl/each_dir.rb +1 -1
- data/lib/gloo/objs/ctrl/each_file.rb +1 -1
- data/lib/gloo/objs/ctrl/each_line.rb +1 -1
- data/lib/gloo/objs/ctrl/each_word.rb +1 -1
- data/lib/gloo/objs/ctrl/repeat.rb +5 -3
- data/lib/gloo/objs/str_utils/cipher.rb +10 -2
- data/lib/gloo/objs/system/file_handle.rb +5 -2
- data/lib/gloo/objs/web/json.rb +7 -5
- data/lib/gloo/persist/declaration_ledger.rb +6 -6
- data/lib/gloo/persist/file_loader.rb +11 -1
- data/lib/gloo/persist/file_storage.rb +8 -2
- data/lib/gloo/persist/persist_man.rb +29 -1
- data/lib/gloo/verbs/break.rb +7 -2
- data/lib/gloo/verbs/context.rb +10 -2
- data/lib/gloo/verbs/eval.rb +5 -4
- data/lib/gloo/verbs/exists.rb +14 -2
- data/lib/gloo/verbs/invoke.rb +7 -6
- data/lib/gloo/verbs/load.rb +16 -2
- data/lib/gloo/verbs/log.rb +8 -4
- data/lib/gloo/verbs/save.rb +10 -3
- data/lib/gloo/verbs/show.rb +15 -10
- data/test.gloo/ctrl/each.test.gloo +21 -7
- data/test.gloo/lang/declaration.test.gloo +7 -2
- data/test.gloo/lang/exceptions.test.gloo +22 -0
- data/test.gloo/lang/gloo_sys.test.gloo +12 -0
- data/test.gloo/lang/naming.test.gloo +7 -2
- data/test.gloo/objs/cipher.test.gloo +27 -0
- data/test.gloo/objs/file.test.gloo +18 -2
- data/test.gloo/objs/fixtures/source_dir_fixture.gloo +6 -0
- data/test.gloo/objs/int.test.gloo +6 -3
- data/test.gloo/objs/json.test.gloo +2 -0
- data/test.gloo/objs/obj.test.gloo +33 -0
- data/test.gloo/objs/string.test.gloo +2 -2
- data/test.gloo/verbs/break.test.gloo +66 -0
- data/test.gloo/verbs/context.test.gloo +12 -0
- data/test.gloo/verbs/eval.test.gloo +8 -0
- data/test.gloo/verbs/exists.test.gloo +19 -0
- data/test.gloo/verbs/invoke.test.gloo +50 -0
- data/test.gloo/verbs/load.test.gloo +101 -2
- data/test.gloo/verbs/log.test.gloo +16 -0
- data/test.gloo/verbs/reload.test.gloo +9 -4
- data/test.gloo/verbs/save.test.gloo +41 -16
- data/test.gloo/verbs/show.test.gloo +32 -0
- metadata +5 -6
|
@@ -191,6 +191,25 @@ module Gloo
|
|
|
191
191
|
return @maps.select { |fs| fs.roots.include?( root ) }
|
|
192
192
|
end
|
|
193
193
|
|
|
194
|
+
#
|
|
195
|
+
# The path of the file an object was loaded from, or nil if it
|
|
196
|
+
# didn't come from one. Walks up from the object to the first one
|
|
197
|
+
# a loaded file declares, so an object created at runtime under a
|
|
198
|
+
# loaded one gets that file. When several files declare it (a
|
|
199
|
+
# namespace container), the first one loaded wins.
|
|
200
|
+
#
|
|
201
|
+
def source_file_of( obj )
|
|
202
|
+
declared = @maps.map { |fs| [ fs, declared_by( fs ) ] }
|
|
203
|
+
o = obj
|
|
204
|
+
while o && !o.root?
|
|
205
|
+
match = declared.find { |_, owned| owned.key?( o ) }
|
|
206
|
+
return match.first.pn if match
|
|
207
|
+
|
|
208
|
+
o = o.parent
|
|
209
|
+
end
|
|
210
|
+
return nil
|
|
211
|
+
end
|
|
212
|
+
|
|
194
213
|
|
|
195
214
|
#
|
|
196
215
|
# Get the full path and name of the file.
|
|
@@ -265,6 +284,15 @@ module Gloo
|
|
|
265
284
|
end
|
|
266
285
|
end
|
|
267
286
|
|
|
287
|
+
#
|
|
288
|
+
# Every heap object fs's SourceDoc declares.
|
|
289
|
+
#
|
|
290
|
+
def declared_by( fs )
|
|
291
|
+
owned = {}.compare_by_identity
|
|
292
|
+
collect_owned( fs.source_doc&.children, owned )
|
|
293
|
+
return owned
|
|
294
|
+
end
|
|
295
|
+
|
|
268
296
|
#
|
|
269
297
|
# Every heap object declared by some loaded file's SourceDoc
|
|
270
298
|
# other than fs.
|
|
@@ -368,7 +396,7 @@ module Gloo
|
|
|
368
396
|
#
|
|
369
397
|
def warn_if_dirty( fs )
|
|
370
398
|
saver = Gloo::Persist::FileSaver.new( @engine, fs.pn, fs.obj, fs.source_doc )
|
|
371
|
-
@engine.
|
|
399
|
+
@engine.warn "#{RELOAD_DIRTY_WARNING}#{fs.pn}" if saver.dirty?
|
|
372
400
|
end
|
|
373
401
|
|
|
374
402
|
end
|
data/lib/gloo/verbs/break.rb
CHANGED
|
@@ -51,11 +51,16 @@ module Gloo
|
|
|
51
51
|
{
|
|
52
52
|
:name => KEYWORD,
|
|
53
53
|
:shortcut => KEYWORD_SHORT,
|
|
54
|
-
:description => 'Break out of the current script, stop running.'
|
|
54
|
+
:description => 'Break out of the current script, stop running. ' \
|
|
55
|
+
'If that script is the do script of a loop (repeat, or any ' \
|
|
56
|
+
'kind of each), the whole loop ends.',
|
|
55
57
|
:syntax => [ 'break' ],
|
|
56
58
|
:result => 'No more commands are executed in the currently ' \
|
|
57
59
|
'running script. If there are other scripts in the stack, ' \
|
|
58
|
-
'they will resume normally.',
|
|
60
|
+
'they will resume normally. In a loop\'s do script, the loop ' \
|
|
61
|
+
'ends too and runs no more passes; the script that ran the ' \
|
|
62
|
+
'loop carries on. A break in a script the do script runs ' \
|
|
63
|
+
'(eg. with run) ends only that script, not the loop.',
|
|
59
64
|
:examples => <<~EXAMPLES.strip
|
|
60
65
|
#
|
|
61
66
|
# Break out of a script.
|
data/lib/gloo/verbs/context.rb
CHANGED
|
@@ -16,6 +16,8 @@ module Gloo
|
|
|
16
16
|
# Run the verb.
|
|
17
17
|
#
|
|
18
18
|
def run
|
|
19
|
+
return unless take_evaluated_arg
|
|
20
|
+
|
|
19
21
|
set_context if @tokens.token_count > 1
|
|
20
22
|
show_context
|
|
21
23
|
end
|
|
@@ -74,9 +76,12 @@ module Gloo
|
|
|
74
76
|
"to that path. Use 'context root' to set the context back to " \
|
|
75
77
|
'the root level. When context has been set, the pathname can ' \
|
|
76
78
|
'start from the context container by use of the @. prefix.',
|
|
77
|
-
:syntax => [ 'context {path.to.new.context}' ],
|
|
79
|
+
:syntax => [ 'context {path.to.new.context}', 'context ( {expression} )' ],
|
|
78
80
|
:parameters => [
|
|
79
|
-
'{path.to.new.context} — Optional. The path to the new context when setting the context.'
|
|
81
|
+
'{path.to.new.context} — Optional. The path to the new context when setting the context.',
|
|
82
|
+
'( {expression} ) — Instead of a literal path, an expression ' \
|
|
83
|
+
'in parentheses: it is worked out and its value is used as ' \
|
|
84
|
+
'the path to the new context.'
|
|
80
85
|
],
|
|
81
86
|
:result => 'Context is optionally set. It will be set to the ' \
|
|
82
87
|
'new context path when we are changing context. Context is ' \
|
|
@@ -100,6 +105,9 @@ module Gloo
|
|
|
100
105
|
@ context.sub
|
|
101
106
|
show @.msg
|
|
102
107
|
@ root
|
|
108
|
+
|
|
109
|
+
# The path can be worked out:
|
|
110
|
+
> context ( 'context.' 'sub' )
|
|
103
111
|
EXAMPLES
|
|
104
112
|
}
|
|
105
113
|
end
|
data/lib/gloo/verbs/eval.rb
CHANGED
|
@@ -30,9 +30,8 @@ module Gloo
|
|
|
30
30
|
#
|
|
31
31
|
def run
|
|
32
32
|
if @tokens.token_count > 1
|
|
33
|
-
|
|
34
|
-
result
|
|
35
|
-
@engine.heap.it.set_to result
|
|
33
|
+
result, ok = evaluate_expr( @tokens.params )
|
|
34
|
+
@engine.heap.it.set_to( ok ? result : false )
|
|
36
35
|
else
|
|
37
36
|
@engine.heap.it.set_to true
|
|
38
37
|
end
|
|
@@ -61,7 +60,9 @@ module Gloo
|
|
|
61
60
|
'{expression} — The expression that will be evaluated. The expression is optional. If not provided, eval will result in true.'
|
|
62
61
|
],
|
|
63
62
|
:result => 'The result of the expression evaluation is put ' \
|
|
64
|
-
'into it. No other action is performed.'
|
|
63
|
+
'into it. No other action is performed. If the expression ' \
|
|
64
|
+
'fails (eg. it names a missing object), the error is ' \
|
|
65
|
+
'reported and it is false.',
|
|
65
66
|
:examples => <<~EXAMPLES.strip,
|
|
66
67
|
> eval "me"
|
|
67
68
|
> eval "hello " "world"
|
data/lib/gloo/verbs/exists.rb
CHANGED
|
@@ -23,6 +23,8 @@ module Gloo
|
|
|
23
23
|
# Run the verb.
|
|
24
24
|
#
|
|
25
25
|
def run
|
|
26
|
+
return @engine.heap.it.set_to( false ) unless take_evaluated_arg
|
|
27
|
+
|
|
26
28
|
if @tokens.token_count == 3
|
|
27
29
|
type = @tokens.second.strip.downcase
|
|
28
30
|
keyword = @tokens.last
|
|
@@ -105,7 +107,10 @@ module Gloo
|
|
|
105
107
|
:description => 'Check to see if a verb or object type has ' \
|
|
106
108
|
'been defined (or loaded), or if an object instance exists ' \
|
|
107
109
|
'in the heap. With no kind, any of the three counts.',
|
|
108
|
-
:syntax => [
|
|
110
|
+
:syntax => [
|
|
111
|
+
'exists? |any,object,verb,instance| {keyword}',
|
|
112
|
+
'exists? |any,object,verb,instance| ( {expression} )'
|
|
113
|
+
],
|
|
109
114
|
:parameters => [
|
|
110
115
|
"Kind of Keyword — Optional; any is the default. " \
|
|
111
116
|
"any checks for a verb, an object type, or an object instance; " \
|
|
@@ -115,7 +120,10 @@ module Gloo
|
|
|
115
120
|
"instance exists at the pathname represented by keyword. " \
|
|
116
121
|
"`exists? any markdown` is identical to `exists? markdown`.",
|
|
117
122
|
'{keyword} — A verb or object type keyword or keyword shortcut, ' \
|
|
118
|
-
'or the pathname to an object instance. Must be specified.'
|
|
123
|
+
'or the pathname to an object instance. Must be specified.',
|
|
124
|
+
'( {expression} ) — Instead of a literal keyword, an expression ' \
|
|
125
|
+
'in parentheses: it is worked out and its value is used as ' \
|
|
126
|
+
'the keyword or pathname.'
|
|
119
127
|
],
|
|
120
128
|
:result => 'If the verb, object type or instance exists, then ' \
|
|
121
129
|
'true, otherwise false. The result will be in it.',
|
|
@@ -142,6 +150,10 @@ module Gloo
|
|
|
142
150
|
|
|
143
151
|
\t\texists? instance exists.on_load
|
|
144
152
|
\t\tshow it # true
|
|
153
|
+
|
|
154
|
+
\t\t# The keyword can be worked out
|
|
155
|
+
\t\texists? object ( 'mark' 'down' )
|
|
156
|
+
\t\tshow it # true
|
|
145
157
|
EXAMPLES
|
|
146
158
|
}
|
|
147
159
|
end
|
data/lib/gloo/verbs/invoke.rb
CHANGED
|
@@ -60,7 +60,8 @@ module Gloo
|
|
|
60
60
|
'{path.to.function} — The function we want to invoke.',
|
|
61
61
|
'{params} — The list of parameters to the function.'
|
|
62
62
|
],
|
|
63
|
-
:result => 'The result of the function is put into it
|
|
63
|
+
:result => 'The result of the function is put into it; if ' \
|
|
64
|
+
'anything goes wrong, it is false. The ' \
|
|
64
65
|
'inline forms (invoke(...), ~>(...)) can be used anywhere ' \
|
|
65
66
|
'an expression is evaluated - show, put ... into, if, ' \
|
|
66
67
|
'unless, eval, and more - and evaluate to the result ' \
|
|
@@ -69,13 +70,13 @@ module Gloo
|
|
|
69
70
|
"#{Gloo::Core::Invoker::NO_TARGET_ERR} — No function reference was given.",
|
|
70
71
|
"Object '{path.to.function}' was not found. — The path doesn't resolve to any object.",
|
|
71
72
|
"#{Gloo::Core::Invoker::NOT_FUNCTION_ERR}{path.to.function} — The path resolves to an object that isn't a function.",
|
|
72
|
-
"#{Gloo::Core::Invoker::PARAM_COUNT_ERR}{path.to.function} (expected N, got N) — The number of params given doesn't match what the function declares."
|
|
73
|
+
"#{Gloo::Core::Invoker::PARAM_COUNT_ERR}{path.to.function} (expected N, got N) — The number of params given doesn't match what the function declares.",
|
|
74
|
+
"Object '{param}' was not found. — A param names an object that doesn't exist. The function isn't run."
|
|
73
75
|
],
|
|
74
76
|
:notes => 'If the function itself fails during invocation ' \
|
|
75
|
-
'(its on_invoke script hits an error), it is
|
|
76
|
-
'
|
|
77
|
-
'
|
|
78
|
-
'failure inside the function. ' \
|
|
77
|
+
'(its on_invoke script hits an error), it is false rather ' \
|
|
78
|
+
'than an unreliable result. The underlying error is ' \
|
|
79
|
+
'whatever was reported by the failure inside the function. ' \
|
|
79
80
|
'Inline call params are space-separated, same as the ' \
|
|
80
81
|
'standalone verb - each one is evaluated as a single ' \
|
|
81
82
|
'token (a literal or object reference), not a multi-token ' \
|
data/lib/gloo/verbs/load.rb
CHANGED
|
@@ -23,6 +23,8 @@ module Gloo
|
|
|
23
23
|
# Run the verb.
|
|
24
24
|
#
|
|
25
25
|
def run
|
|
26
|
+
return unless take_evaluated_arg
|
|
27
|
+
|
|
26
28
|
if @tokens.token_count == 2
|
|
27
29
|
opt = FILE_OPT
|
|
28
30
|
fn = @tokens.second
|
|
@@ -109,13 +111,18 @@ module Gloo
|
|
|
109
111
|
:syntax => [
|
|
110
112
|
'load {file_name}',
|
|
111
113
|
'load ext {my_ext}',
|
|
112
|
-
'load lib {lib_name}'
|
|
114
|
+
'load lib {lib_name}',
|
|
115
|
+
'load [ext|lib] ( {expression} )'
|
|
113
116
|
],
|
|
114
117
|
:parameters => [
|
|
115
118
|
'{file_name} — Name of the object file that is to be loaded.',
|
|
116
119
|
'reference type — By default, the type is file. File can be ' \
|
|
117
120
|
'specified, but does not need to be. Use ext to load a ' \
|
|
118
|
-
'User Extension. Use lib to load a Core Library.'
|
|
121
|
+
'User Extension. Use lib to load a Core Library.',
|
|
122
|
+
'( {expression} ) — Instead of a literal name, an expression ' \
|
|
123
|
+
'in parentheses: it is worked out and its value is used as ' \
|
|
124
|
+
'the file, extension or library name. Wildcards and the ' \
|
|
125
|
+
'.gloo extension work as they do for a literal name.'
|
|
119
126
|
],
|
|
120
127
|
:result => 'Objects are loaded into the heap. on_load scripts ' \
|
|
121
128
|
'are run within the loaded objects.',
|
|
@@ -132,6 +139,9 @@ module Gloo
|
|
|
132
139
|
'they merge in the heap. If two loaded files declare the ' \
|
|
133
140
|
'same object with different values, the first one loaded ' \
|
|
134
141
|
"wins and a warning is logged.\n\n" \
|
|
142
|
+
'A file with no objects in it (empty, comments only, or ' \
|
|
143
|
+
"only 'load lib' lines) loads nothing and logs a warning " \
|
|
144
|
+
"naming the file; it isn't an error.\n\n" \
|
|
135
145
|
'A contiguous run of whole-line comments declared ' \
|
|
136
146
|
'immediately above an object (same indent, no blank line ' \
|
|
137
147
|
"in between) becomes that object's doc, captured as the " \
|
|
@@ -145,6 +155,10 @@ module Gloo
|
|
|
145
155
|
> load my/app/*
|
|
146
156
|
> load ~/.my_app/settings.gloo
|
|
147
157
|
|
|
158
|
+
# Load a file whose name is worked out
|
|
159
|
+
> load ( $.gloo_tmp '/scratch.gloo' )
|
|
160
|
+
> load ( my.file_obj )
|
|
161
|
+
|
|
148
162
|
# Load a User Extension
|
|
149
163
|
> load ext my_ext
|
|
150
164
|
|
data/lib/gloo/verbs/log.rb
CHANGED
|
@@ -50,11 +50,12 @@ module Gloo
|
|
|
50
50
|
# Write to the specified logger.
|
|
51
51
|
#
|
|
52
52
|
def write_to_log
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
# A failed expression is still logged as far as it got, but it
|
|
54
|
+
# is false.
|
|
55
|
+
result, ok = evaluate_expr( @tokens.params )
|
|
55
56
|
level = log_level_specified( result )
|
|
56
57
|
@engine.log.write result, level
|
|
57
|
-
@engine.heap.it.set_to result
|
|
58
|
+
@engine.heap.it.set_to( ok ? result : false )
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
#
|
|
@@ -106,7 +107,10 @@ module Gloo
|
|
|
106
107
|
'not specified, the message is written as DEBUG level.'
|
|
107
108
|
],
|
|
108
109
|
:result => 'The message is written to the log at the ' \
|
|
109
|
-
'specified level. It will contain the log message.'
|
|
110
|
+
'specified level. It will contain the log message. If the ' \
|
|
111
|
+
'expression fails, the error is reported and what was ' \
|
|
112
|
+
'worked out so far is still logged, but it is false. A ' \
|
|
113
|
+
'level that can\'t be worked out falls back to debug.',
|
|
110
114
|
:notes => "Use `log clear` from the CLI to clear out both the " \
|
|
111
115
|
"standard log and the error log.\n\n" \
|
|
112
116
|
"> log clear",
|
data/lib/gloo/verbs/save.rb
CHANGED
|
@@ -48,9 +48,11 @@ module Gloo
|
|
|
48
48
|
# Run 'save {obj} to {path}'.
|
|
49
49
|
#
|
|
50
50
|
def run_save_to
|
|
51
|
+
return unless take_evaluated_arg
|
|
52
|
+
|
|
51
53
|
name = @tokens.before_token( TO )[ 1 ]
|
|
52
54
|
path = @tokens.after_token( TO )
|
|
53
|
-
|
|
55
|
+
if path.blank?
|
|
54
56
|
@engine.syntax_err MISSING_PATH_ERR
|
|
55
57
|
return
|
|
56
58
|
end
|
|
@@ -84,13 +86,17 @@ module Gloo
|
|
|
84
86
|
:syntax => [
|
|
85
87
|
'save',
|
|
86
88
|
'save {path.to.object}',
|
|
87
|
-
'save {path.to.object} to {path}'
|
|
89
|
+
'save {path.to.object} to {path}',
|
|
90
|
+
'save {path.to.object} to ( {expression} )'
|
|
88
91
|
],
|
|
89
92
|
:parameters => [
|
|
90
93
|
'{path.to.object} — Name of the object to save.',
|
|
91
94
|
'{path} — Where to save it, relative to the project root ' \
|
|
92
95
|
'(.gloo appended if omitted). Registers the mapping, so ' \
|
|
93
|
-
'a later bare save includes it.'
|
|
96
|
+
'a later bare save includes it.',
|
|
97
|
+
'( {expression} ) — Instead of a literal path, an expression ' \
|
|
98
|
+
'in parentheses: it is worked out and its value is used as ' \
|
|
99
|
+
'the path.'
|
|
94
100
|
],
|
|
95
101
|
:result => 'The file(s) are updated with the latest object ' \
|
|
96
102
|
'state. This is a rewrite, not a regeneration: comments, ' \
|
|
@@ -129,6 +135,7 @@ module Gloo
|
|
|
129
135
|
> save my_obj
|
|
130
136
|
> save my_obj to sub/my_obj
|
|
131
137
|
> save app.core.settings to config/settings
|
|
138
|
+
> save my_obj to ( $.gloo_tmp '/my_obj' )
|
|
132
139
|
EXAMPLES
|
|
133
140
|
}
|
|
134
141
|
end
|
data/lib/gloo/verbs/show.rb
CHANGED
|
@@ -16,10 +16,11 @@ module Gloo
|
|
|
16
16
|
#
|
|
17
17
|
def run
|
|
18
18
|
if @tokens.token_count > 1
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
# A failed expression is still shown as far as it got (a best
|
|
20
|
+
# guess, and handy when debugging), but it is false.
|
|
21
|
+
result, ok = evaluate_expr( @tokens.params )
|
|
21
22
|
@engine.log.show get_formatted_string( result )
|
|
22
|
-
@engine.heap.it.set_to result
|
|
23
|
+
@engine.heap.it.set_to( ok ? result : false )
|
|
23
24
|
else
|
|
24
25
|
@engine.log.show ''
|
|
25
26
|
end
|
|
@@ -46,14 +47,16 @@ module Gloo
|
|
|
46
47
|
private
|
|
47
48
|
|
|
48
49
|
#
|
|
49
|
-
# Get the formatted string
|
|
50
|
+
# Get the formatted string: in the color given in the params, if
|
|
51
|
+
# any. A color that can't be worked out (already reported) shows
|
|
52
|
+
# the string plain.
|
|
50
53
|
#
|
|
51
54
|
def get_formatted_string( str )
|
|
52
55
|
if @params&.token_count&.positive?
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return @engine.platform.get_colorized_string( str,
|
|
56
|
+
val, ok = evaluate_expr( @params.tokens )
|
|
57
|
+
return str unless ok && val
|
|
58
|
+
|
|
59
|
+
return @engine.platform.get_colorized_string( str, val.to_sym )
|
|
57
60
|
end
|
|
58
61
|
return str
|
|
59
62
|
end
|
|
@@ -79,11 +82,13 @@ module Gloo
|
|
|
79
82
|
'{target} — The object that we want to see. The target ' \
|
|
80
83
|
'might be the path to an object, a literal or an ' \
|
|
81
84
|
'expression to be evaluated before being shown.',
|
|
82
|
-
'{color} — Optional color for the text.'
|
|
85
|
+
'{color} — Optional color for the text. If the color can\'t be worked out, the error is reported and the text is shown plain.'
|
|
83
86
|
],
|
|
84
87
|
:result => "The object's value is shown, or the literal, or " \
|
|
85
88
|
'the result of the evaluated expression is shown. It will ' \
|
|
86
|
-
"contain the object's value."
|
|
89
|
+
"contain the object's value. If the expression fails (eg. " \
|
|
90
|
+
'it names a missing object), the error is reported and ' \
|
|
91
|
+
'what was worked out so far is still shown, but it is false.',
|
|
87
92
|
:examples => <<~EXAMPLES.strip,
|
|
88
93
|
> show "me"
|
|
89
94
|
> show "hello " "world"
|
|
@@ -38,23 +38,37 @@ tests.ctrl.each [can] :
|
|
|
38
38
|
|
|
39
39
|
#
|
|
40
40
|
# Each file / each dir over a nested folder tree.
|
|
41
|
-
# The tree is built
|
|
41
|
+
# The tree is built in $.gloo_tmp and removed at the end. It's built
|
|
42
|
+
# and removed with shell commands, so these tests need a Unix shell.
|
|
42
43
|
#
|
|
43
44
|
|
|
44
45
|
tests.ctrl.each_folder [can] :
|
|
45
46
|
|
|
46
47
|
count [int] : 0
|
|
48
|
+
walk_name [string] : /gloo_each_walk
|
|
49
|
+
walk [string] :
|
|
47
50
|
|
|
48
51
|
make_tree [system] :
|
|
49
|
-
command [string] :
|
|
52
|
+
command [string] :
|
|
50
53
|
get_output [bool] : false
|
|
51
54
|
remove_tree [system] :
|
|
52
|
-
command [string] :
|
|
55
|
+
command [string] :
|
|
53
56
|
get_output [bool] : false
|
|
54
57
|
|
|
58
|
+
# The tree lives in the gloo tmp folder ($.gloo_tmp); paths are
|
|
59
|
+
# quoted in the commands in case that folder has spaces.
|
|
60
|
+
on_load [script] :
|
|
61
|
+
put $.gloo_tmp ^.walk_name into ^.walk
|
|
62
|
+
put "rm -rf '" ^.walk "' && mkdir -p '" ^.walk "/a/deep' '" ^.walk "/.obs' && cd '" ^.walk "' && touch top.md notes.txt a/x.md a/deep/Y.MD .obs/o.md" into ^.make_tree.command
|
|
63
|
+
put "rm -rf '" ^.walk "'" into ^.remove_tree.command
|
|
64
|
+
put ^.walk into ^.files.in
|
|
65
|
+
put ^.walk '/**/' into ^.wild.in
|
|
66
|
+
put ^.walk into ^.top.in
|
|
67
|
+
put ^.walk into ^.dirs.in
|
|
68
|
+
|
|
55
69
|
files [each] :
|
|
56
70
|
file [file] :
|
|
57
|
-
in [file] :
|
|
71
|
+
in [file] :
|
|
58
72
|
ext [string] : md
|
|
59
73
|
recursive [bool] : true
|
|
60
74
|
do [script] :
|
|
@@ -63,7 +77,7 @@ tests.ctrl.each_folder [can] :
|
|
|
63
77
|
|
|
64
78
|
wild [each] :
|
|
65
79
|
file [file] :
|
|
66
|
-
in [file] :
|
|
80
|
+
in [file] :
|
|
67
81
|
ext [string] : md
|
|
68
82
|
do [script] :
|
|
69
83
|
eval ^^.count + 1
|
|
@@ -71,14 +85,14 @@ tests.ctrl.each_folder [can] :
|
|
|
71
85
|
|
|
72
86
|
top [each] :
|
|
73
87
|
file [file] :
|
|
74
|
-
in [file] :
|
|
88
|
+
in [file] :
|
|
75
89
|
do [script] :
|
|
76
90
|
eval ^^.count + 1
|
|
77
91
|
put it into ^^.count
|
|
78
92
|
|
|
79
93
|
dirs [each] :
|
|
80
94
|
dir [file] :
|
|
81
|
-
in [file] :
|
|
95
|
+
in [file] :
|
|
82
96
|
recursive [bool] : true
|
|
83
97
|
do [script] :
|
|
84
98
|
eval ^^.count + 1
|
|
@@ -7,7 +7,12 @@ tests.lang.declaration [can] :
|
|
|
7
7
|
# A fixture deliberately missing the ']' after its type, loaded inside
|
|
8
8
|
# its test so the syntax error it reports belongs to that test.
|
|
9
9
|
# Loading reads the type as 'int' anyway.
|
|
10
|
-
unclosed_file [file] :
|
|
10
|
+
unclosed_file [file] :
|
|
11
|
+
|
|
12
|
+
# Scratch files live in the gloo tmp folder ($.gloo_tmp).
|
|
13
|
+
on_load [script] :
|
|
14
|
+
put $.gloo_tmp '/gloo_declaration_test.gloo' into ^.unclosed_file
|
|
15
|
+
|
|
11
16
|
unclosed_src [text] : BEGIN
|
|
12
17
|
declaration_fixture [container] :
|
|
13
18
|
unclosed [int : 7
|
|
@@ -20,7 +25,7 @@ END
|
|
|
20
25
|
expect_errors [bool] : true
|
|
21
26
|
on_test [script] :
|
|
22
27
|
tell ^^.unclosed_file to write ( ^^.unclosed_src )
|
|
23
|
-
load
|
|
28
|
+
load ( ^^.unclosed_file )
|
|
24
29
|
eval declaration_fixture.unclosed = 7
|
|
25
30
|
assert "expected the value 7"
|
|
26
31
|
|
|
@@ -23,6 +23,18 @@ tests.lang.exceptions [can] :
|
|
|
23
23
|
kind [string] :
|
|
24
24
|
location [string] :
|
|
25
25
|
|
|
26
|
+
# A scratch file with a mistake in it, for the location of an error in
|
|
27
|
+
# a loaded file.
|
|
28
|
+
broken_file [file] :
|
|
29
|
+
broken_name [string] : /gloo_exceptions_location.gloo
|
|
30
|
+
broken_src [text] : BEGIN
|
|
31
|
+
broken_location_demo [can] :
|
|
32
|
+
count [int : 3
|
|
33
|
+
END
|
|
34
|
+
|
|
35
|
+
on_load [script] :
|
|
36
|
+
put $.gloo_tmp ^.broken_name into ^.broken_file
|
|
37
|
+
|
|
26
38
|
confirm_throw_fires_on_exception [test] :
|
|
27
39
|
description [string] : Confirm throw fires on_exception and populates exception_data, but does not fire on_error.
|
|
28
40
|
expect_errors [bool] : true
|
|
@@ -127,3 +139,13 @@ tests.lang.exceptions [can] :
|
|
|
127
139
|
eval ^^.num = 0
|
|
128
140
|
assert "expected the best guess, 0"
|
|
129
141
|
|
|
142
|
+
confirm_error_location_in_a_loaded_file [test] :
|
|
143
|
+
description [string] : an error in a file a script loads names that file and line, not the script.
|
|
144
|
+
expect_errors [bool] : true
|
|
145
|
+
on_test [script] :
|
|
146
|
+
tell ^^.broken_file to write ( ^^.broken_src )
|
|
147
|
+
load ( ^^.broken_file )
|
|
148
|
+
check ^^.error_data.location for ends_with? ( 'gloo_exceptions_location.gloo:2' )
|
|
149
|
+
assert "expected the location to be the loaded file and line"
|
|
150
|
+
tell broken_location_demo to unload
|
|
151
|
+
tell ^^.broken_file to delete
|
|
@@ -79,6 +79,18 @@ tests.lang.gloo_sys [can] :
|
|
|
79
79
|
eval it = ''
|
|
80
80
|
refute "expected gloo_log to not be empty"
|
|
81
81
|
|
|
82
|
+
get_gloo_tmp [test] :
|
|
83
|
+
description [string] : $.gloo_tmp is a folder that exists.
|
|
84
|
+
f [file] :
|
|
85
|
+
on_test [script] :
|
|
86
|
+
eval $.gloo_tmp
|
|
87
|
+
eval it = ''
|
|
88
|
+
refute "expected gloo_tmp to not be empty"
|
|
89
|
+
|
|
90
|
+
put $.gloo_tmp into ^.f
|
|
91
|
+
check ^.f for is_dir?
|
|
92
|
+
assert "expected gloo_tmp to be an existing folder"
|
|
93
|
+
|
|
82
94
|
get_screen_dims [test] :
|
|
83
95
|
description [string] : Get the screen dimensions.
|
|
84
96
|
i [int] :
|
|
@@ -16,7 +16,12 @@ tests.lang.naming [can] :
|
|
|
16
16
|
|
|
17
17
|
# A fixture with a deliberately nameless object, loaded inside its
|
|
18
18
|
# test so the syntax error it reports belongs to that test.
|
|
19
|
-
nameless_file [file] :
|
|
19
|
+
nameless_file [file] :
|
|
20
|
+
|
|
21
|
+
# Scratch files live in the gloo tmp folder ($.gloo_tmp).
|
|
22
|
+
on_load [script] :
|
|
23
|
+
put $.gloo_tmp '/gloo_naming_test.gloo' into ^.nameless_file
|
|
24
|
+
|
|
20
25
|
nameless_src [text] : BEGIN
|
|
21
26
|
naming_fixture [container] :
|
|
22
27
|
[string] : no name given
|
|
@@ -54,7 +59,7 @@ END
|
|
|
54
59
|
expect_errors [bool] : true
|
|
55
60
|
on_test [script] :
|
|
56
61
|
tell ^^.nameless_file to write ( ^^.nameless_src )
|
|
57
|
-
load
|
|
62
|
+
load ( ^^.nameless_file )
|
|
58
63
|
eval naming_fixture.unnamed_1 = 'no name given'
|
|
59
64
|
assert "expected the nameless object to be reached as unnamed_1"
|
|
60
65
|
|
|
@@ -41,3 +41,30 @@ tests.objs.cipher [can] :
|
|
|
41
41
|
tell tests.objs.cipher.b to decrypt
|
|
42
42
|
eval tests.objs.cipher.b.data = ^^.plaintext
|
|
43
43
|
assert "expected the second cipher to decrypt with the shared key"
|
|
44
|
+
|
|
45
|
+
# Fixed values, so the results never vary: 'hello' encrypted with key
|
|
46
|
+
# ('k' x 32) and iv ('i' x 16), and a wrong key ('0' x 32) whose
|
|
47
|
+
# garbage fails the padding check. (AES-CBC can't always tell a key is
|
|
48
|
+
# wrong: about 1 wrong key in 256 gives garbage with no error.)
|
|
49
|
+
fixed_iv [string] : aWlpaWlpaWlpaWlpaWlpaQ==
|
|
50
|
+
hello_encrypted [string] : aRyXOIIhJzBhh+BthfFstw==
|
|
51
|
+
wrong_key [string] : MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA=
|
|
52
|
+
err_fired [bool] : false
|
|
53
|
+
|
|
54
|
+
on_error [script] :
|
|
55
|
+
put true into ^.err_fired
|
|
56
|
+
|
|
57
|
+
wrong_key_is_an_error [test] :
|
|
58
|
+
description [string] : decrypt with a wrong key that fails the padding check reports an error.
|
|
59
|
+
expect_errors [bool] : true
|
|
60
|
+
on_test [script] :
|
|
61
|
+
put false into ^^.err_fired
|
|
62
|
+
create tests.objs.cipher.w as cipher
|
|
63
|
+
put ^^.wrong_key into tests.objs.cipher.w.key
|
|
64
|
+
put ^^.fixed_iv into tests.objs.cipher.w.init_vector
|
|
65
|
+
put ^^.hello_encrypted into tests.objs.cipher.w.data
|
|
66
|
+
tell tests.objs.cipher.w to decrypt
|
|
67
|
+
eval it = false
|
|
68
|
+
assert "expected it to be false"
|
|
69
|
+
eval ^^.err_fired = true
|
|
70
|
+
assert "expected on_error to fire for the wrong key"
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
# File object tests
|
|
3
3
|
#
|
|
4
4
|
# The file object's value is a path. These tests write a scratch
|
|
5
|
-
# file
|
|
5
|
+
# file in $.gloo_tmp, exercise it, and delete it at the end.
|
|
6
6
|
#
|
|
7
7
|
|
|
8
8
|
tests.objs.file [can] :
|
|
9
9
|
|
|
10
|
-
f [file] :
|
|
10
|
+
f [file] :
|
|
11
|
+
|
|
12
|
+
# Scratch files live in the gloo tmp folder ($.gloo_tmp).
|
|
13
|
+
on_load [script] :
|
|
14
|
+
put $.gloo_tmp '/gloo_file_test.txt' into ^.f
|
|
15
|
+
|
|
11
16
|
names [file] : /Users/me/project/report.csv
|
|
12
17
|
|
|
13
18
|
first [string] : first line
|
|
@@ -29,11 +34,22 @@ tests.objs.file [can] :
|
|
|
29
34
|
refute "expected the file not to be a directory"
|
|
30
35
|
|
|
31
36
|
tell ^^.f to read ( ^^.back )
|
|
37
|
+
eval it = true
|
|
38
|
+
assert "expected read into a destination to put true in it"
|
|
32
39
|
eval ^^.back = 'first line'
|
|
33
40
|
assert "expected read to return what was written"
|
|
34
41
|
|
|
35
42
|
tell ^^.f to delete
|
|
36
43
|
|
|
44
|
+
read_without_a_destination_puts_the_data_in_it [test] :
|
|
45
|
+
description [string] : read with no destination puts the file's contents in it.
|
|
46
|
+
on_test [script] :
|
|
47
|
+
tell ^^.f to write ( ^^.first )
|
|
48
|
+
tell ^^.f to read
|
|
49
|
+
eval it = 'first line'
|
|
50
|
+
assert "expected the file's contents in it"
|
|
51
|
+
tell ^^.f to delete
|
|
52
|
+
|
|
37
53
|
append_adds_a_line [test] :
|
|
38
54
|
description [string] : append adds a new line to the file.
|
|
39
55
|
on_test [script] :
|
|
@@ -48,10 +48,13 @@ tests.objs.int [can] :
|
|
|
48
48
|
eval ^^.a <= 100
|
|
49
49
|
assert "expected it to be less than or equal to 100"
|
|
50
50
|
|
|
51
|
+
# A random value is only checked for its range: two random values
|
|
52
|
+
# are equal now and then, so 'not equal' would fail at random.
|
|
51
53
|
tell ^^.a to randomize (10000)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
eval ^^.a >= 0
|
|
55
|
+
assert "expected it to be greater than or equal to 0"
|
|
56
|
+
eval ^^.a < 10000
|
|
57
|
+
assert "expected it to be less than 10000"
|
|
55
58
|
|
|
56
59
|
format_int [test] :
|
|
57
60
|
description [string] : Format an integer
|
|
@@ -43,6 +43,8 @@ tests.objs.json [can] :
|
|
|
43
43
|
description [string] : parse builds an object tree from the JSON.
|
|
44
44
|
on_test [script] :
|
|
45
45
|
tell ^^.payload to parse ( tests.objs.json.out )
|
|
46
|
+
eval it = true
|
|
47
|
+
assert "expected parse to put true in it"
|
|
46
48
|
|
|
47
49
|
eval tests.objs.json.out.title = 'Brute Force'
|
|
48
50
|
assert "expected parse to set the title"
|