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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 44699b19dc942762560f80854fd4761359dd663d0a154680db6a5e58a6afc442
|
|
4
|
+
data.tar.gz: 3326a1a25aa22a42ea296ee929a2297acd9de944fd700299cb8a833d86df9e73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ccdd1461cdec1c6c6aa1f3a698ab0804c3f86e77cf108ce23c10fd83e5db7ca7107bb0c1823f42af7adbfe8eaa14c4ba7c672bfef8ab06994bbe96dbbe53f7b
|
|
7
|
+
data.tar.gz: f67ee6091000e498da19444ef39ab7b194b2e069dc830c41660bb3090183ce727c0cb43dba6d3cae3b6bcfef551010467634c1a40a4041d19bb9a17294f8be7e
|
data/CLAUDE.md
CHANGED
|
@@ -31,6 +31,7 @@ This project is the authoritative source for gloo language documentation — not
|
|
|
31
31
|
|
|
32
32
|
- **Narrative reference** — `docs/*.md`, eleven flat files: `getting_started.md`, `application.md`, `language_objects.md`, `language_syntax.md`, `language_scripting.md`, `operators.md`, `iterators.md`, `objects.md`, `verbs.md`, `plugins.md`, `web_app.md`. Linked from the root `README.md`. Convention: no markdown link syntax between pages (plain-text pointers instead, e.g. "see Put" not `[Put](put.md)`) since these are read both on GitHub and in a terminal; `---` separates sections that came from different source material.
|
|
33
33
|
- **Verb/object reference** — each verb (`lib/gloo/verbs/*.rb`) and object type (`lib/gloo/objs/**/*.rb`) class defines `self.doc_data` (name, shortcut, description, syntax, parameters, result, errors, examples, notes — see `lib/gloo/docs/doc_data.rb`), rendered by the in-app interactive help shell (`help`/`?`, see `lib/gloo/docs/help_shell.rb`). When adding or changing a verb or object type, add/update its `doc_data` too.
|
|
34
|
+
- **Error handling** — report problems with `@engine.syntax_err` (couldn't be understood), `@engine.err` (understood, couldn't be done) or `@engine.warn` (done, but probably not what was meant); a question answered "no" is just a result. Never `@engine.log.error`/`log.warn` for these (scripts can't see them). "Not found" messages use `Gloo::Core::NotFound`. See `docs/language_syntax.md`, Errors and Warnings.
|
|
34
35
|
- Core-lib gems (`gloo_core_libraries`) follow the same `doc_data` pattern for their own verbs/objects; a narrative `doc/` folder per gem is planned but not yet built.
|
|
35
36
|
|
|
36
37
|
## Test Suites
|
data/docs/application.md
CHANGED
|
@@ -30,7 +30,7 @@ gloo [global option] [file]
|
|
|
30
30
|
|
|
31
31
|
Running gloo with a file specified will run that file. Once that file is done, gloo will quit. However, by specifying `--cli`, once the file has finished, gloo will remain open in CLI mode.
|
|
32
32
|
|
|
33
|
-
If a file named on the command line can't be found (a typo, the wrong folder, a missing `.gloo` extension on a full path), gloo prints `File not found
|
|
33
|
+
If a file named on the command line can't be found (a typo, the wrong folder, a missing `.gloo` extension on a full path), gloo prints `File '{name}' was not found.` to stderr and exits with a non-zero status. Any other load or run error also exits non-zero — including syntax errors in a file that otherwise loaded (gloo keeps loading past them, reporting each with its file and line) — so a shell script calling gloo can tell a failed run from a clean one.
|
|
34
34
|
|
|
35
35
|
When specifying a file there are a couple ways to reference the gloo file to open:
|
|
36
36
|
|
|
@@ -148,6 +148,8 @@ Debug messages are written to the log only, but other messages are also written
|
|
|
148
148
|
|
|
149
149
|
Only error and warning level messages are written to the error log.
|
|
150
150
|
|
|
151
|
+
In test mode (`--test`), errors and warnings go to the log files only, not the console, so the test output is just the test results. An error a test expects (or one from a test file that deliberately contains a problem) is still in `error.log`.
|
|
152
|
+
|
|
151
153
|
The application logs folder is in the gloo folder. When gloo is run, the log files will be created if they do not exist. To trim the logs, just delete those log files.
|
|
152
154
|
|
|
153
155
|
Example tail command to watch the gloo log:
|
data/docs/iterators.md
CHANGED
|
@@ -103,9 +103,11 @@ Children:
|
|
|
103
103
|
- `dir` (file — a directory)
|
|
104
104
|
- The directory instance.
|
|
105
105
|
- `in` (file — a directory)
|
|
106
|
-
- The folder (directory) we will look in for directories.
|
|
106
|
+
- The folder (directory) we will look in for directories. The trailing slash is optional, a leading `~` means your home folder, and the path may include wildcards (see Walking a Folder Tree, under Each File).
|
|
107
107
|
- `do` (script)
|
|
108
108
|
- The action we want to perform for each directory in the folder.
|
|
109
|
+
- `recursive` (bool)
|
|
110
|
+
- Optional. Walk the subfolders too, listing every folder in the tree. Default false.
|
|
109
111
|
|
|
110
112
|
Messages:
|
|
111
113
|
|
|
@@ -140,11 +142,15 @@ Children:
|
|
|
140
142
|
- `file` (file)
|
|
141
143
|
- The file instance.
|
|
142
144
|
- `in` (file)
|
|
143
|
-
- The folder (directory) we will look in for files.
|
|
145
|
+
- The folder (directory) we will look in for files. The trailing slash is optional, a leading `~` means your home folder, and the path may include wildcards (see Walking a Folder Tree, below).
|
|
144
146
|
- `do` (script)
|
|
145
147
|
- The action we want to perform for each file in the folder.
|
|
146
148
|
- `ext` (string)
|
|
147
|
-
- Optional file extension. Limit to files of this kind.
|
|
149
|
+
- Optional file extension. Limit to files of this kind. The match ignores case, so `md` also finds `Notes.MD`, and a leading dot is optional.
|
|
150
|
+
- `recursive` (bool)
|
|
151
|
+
- Optional. Walk the subfolders too. Default false.
|
|
152
|
+
- `include_dirs` (bool)
|
|
153
|
+
- Optional. List folders as well as files. Default false: `each file` gives only files. Works with or without `recursive`.
|
|
148
154
|
|
|
149
155
|
Messages:
|
|
150
156
|
|
|
@@ -169,6 +175,36 @@ each_file [can] :
|
|
|
169
175
|
tell each_file.for to run
|
|
170
176
|
```
|
|
171
177
|
|
|
178
|
+
### Walking a Folder Tree
|
|
179
|
+
|
|
180
|
+
To go through every file in a folder and all its subfolders, set `recursive`:
|
|
181
|
+
|
|
182
|
+
```gloo
|
|
183
|
+
notes [each] :
|
|
184
|
+
file [file] :
|
|
185
|
+
in [file] : /my/notes/
|
|
186
|
+
ext [string] : md
|
|
187
|
+
recursive [bool] : true
|
|
188
|
+
do [script] : show ^.file
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`each dir` takes `recursive` too, and then lists every folder in the tree (but not the `in` folder itself).
|
|
192
|
+
|
|
193
|
+
For more control, put wildcards in the `in` path instead. They are the same wildcards the shell uses:
|
|
194
|
+
|
|
195
|
+
- `in: /my/notes/**/` — every folder in the tree; the same as `recursive: true`.
|
|
196
|
+
- `in: /projects/*/docs/` — the `docs` folder of each project, one level down.
|
|
197
|
+
- `in: /projects/*/` — each folder one level down, but not the top folder.
|
|
198
|
+
|
|
199
|
+
Wildcards combine with `ext`, `recursive` and `include_dirs`. For example, `in: /projects/*/docs/` with `recursive: true` walks every project's docs tree.
|
|
200
|
+
|
|
201
|
+
What the walk gives:
|
|
202
|
+
|
|
203
|
+
- Full paths, sorted, with each folder listed before what is in it.
|
|
204
|
+
- Hidden files and folders (names starting with `.`, such as `.git/`) are skipped.
|
|
205
|
+
- A `**` or `recursive` walk does not go into symlinked folders, so a link loop can't trap it. A symlinked folder is still listed itself by `each dir`, or by `each file` with `include_dirs`, and a single-level `*` in the path does go through it.
|
|
206
|
+
- A missing `in` folder is an error for `each dir`, unless the path includes wildcards (then it just finds nothing). `each file` finds nothing either way.
|
|
207
|
+
|
|
172
208
|
See also: Object Base, Each, Each Line, Each Word, Each Child, Each Directory.
|
|
173
209
|
|
|
174
210
|
## Each Line
|
data/docs/language_syntax.md
CHANGED
|
@@ -43,7 +43,22 @@ colors [can] :
|
|
|
43
43
|
|
|
44
44
|
See also: Show.
|
|
45
45
|
|
|
46
|
-
## Errors
|
|
46
|
+
## Errors and Warnings
|
|
47
|
+
|
|
48
|
+
Gloo makes a best guess and keeps going when it can, but it never does so silently: every problem is reported, in one of these ways.
|
|
49
|
+
|
|
50
|
+
- **Syntax error** — the command couldn't be understood: an unknown verb or object type, a required part missing (`put 3 into`), the wrong number of arguments, an unclosed quote or parenthesis, or an operator missing a value (`show 1 +`).
|
|
51
|
+
- **Runtime error** — the command was understood but couldn't be done: an object that doesn't exist used as a value or a target (`show no.such.obj`), division by zero, a file that can't be read or written, a position out of range. This includes failures caused by the input or the outside world: JSON that doesn't parse, a URL that's malformed or can't be reached, a wrong encryption key, a template with a mistake in it, a date object whose value isn't a date. These read `Could not {what}: {why}.` — for example `Could not parse the JSON in j: expected object key, got 'bad' at line 1 column 2`. A command that would have set `it` sets it to `false` (see It).
|
|
52
|
+
- **Warning** — the command was done, but probably not the way it was meant: a value that isn't really of its type, used as a best guess (`'x' is not an integer; using 0.`), or indentation that doesn't line up. A warning is only logged; it isn't an error.
|
|
53
|
+
- **Just a result** — a question whose answer is "no" is not an error at all: `exists?`, `contains?`, `substring?`, and `index_of` (which gives -1) answer in `it`.
|
|
54
|
+
|
|
55
|
+
Errors are logged with where they happened in front of the message: the file and line while a file is loading (`app.gloo:12: Unknown type 'strng'; using untyped.`), or the script and its line (`app.on_load, line 3: Object 'app.nme' was not found.`). "Not found" errors always read the same way: `Object 'x' was not found.`, `File 'x' was not found.`, `Folder 'x' was not found.`, `Verb 'x' was not found.`
|
|
56
|
+
|
|
57
|
+
What happens after an error:
|
|
58
|
+
|
|
59
|
+
- The line that failed is abandoned, and the script continues with the next line. A `put` whose value couldn't be worked out leaves its target unchanged (and `it` is `false`).
|
|
60
|
+
- Loading a file keeps going after a syntax error, so every problem in the file is reported at once. An object with an unknown type is created untyped, so anything nested under it still loads where it should. A declaration with no name (`[int] : 3`) is created under a placeholder name, `unnamed_1`, `unnamed_2` and so on. A type missing its closing bracket (`count [int : 3`) is read as if it were closed after the type word.
|
|
61
|
+
- The error runs any `on_error` handler (see Events below).
|
|
47
62
|
|
|
48
63
|
Gloo has a special `error` variable that's not part of the normal object heap. The error will be empty most of the time, but if a command results in an error, this variable will hold the error message until the next command is executed. The error is a string and can be accessed by simply referring to the path-name `error`.
|
|
49
64
|
|
|
@@ -88,10 +103,17 @@ The following events are application and file-level events:
|
|
|
88
103
|
- `on_quit` — event triggered when gloo is quitting
|
|
89
104
|
- `on_save` — when an object is saved, this event is triggered
|
|
90
105
|
- `on_reload` — event triggered when an object receives message to reload
|
|
91
|
-
- `on_error` — event triggered when gloo
|
|
92
|
-
- `on_exception` — event triggered when gloo's safety net catches an unanticipated Ruby exception
|
|
106
|
+
- `on_error` — event triggered when gloo reports a syntax or runtime error (see Errors and Warnings). Warnings don't trigger it.
|
|
107
|
+
- `on_exception` — event triggered when gloo's safety net catches an unanticipated Ruby exception — a bug in gloo itself, since anticipated failures such as division by zero or a missing file are runtime errors (use the `throw` verb to exercise it)
|
|
108
|
+
|
|
109
|
+
`on_error` and `on_exception` are independent channels — a gloo error fires `on_error` only, an unhandled Ruby exception fires `on_exception` only, and neither triggers the other. In both cases the line that failed is abandoned and execution continues with the next line; a handler is a place to log or react, not a way to retry. After the handler runs, the error is still there for the rest of the command to see (`error`).
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
Each handler reads its details from a sibling data container (`error_data` / `exception_data`) that the engine populates before running the script; the handler script and its data container can sit at the root of a file or be nested together inside a container. Each child is optional — the engine fills in the ones that are there:
|
|
112
|
+
|
|
113
|
+
- `message` — the error message
|
|
114
|
+
- `backtrace` — the backtrace, when there is one
|
|
115
|
+
- `kind` (`error_data` only) — `syntax` if the command couldn't be understood, `runtime` if it couldn't be done
|
|
116
|
+
- `location` (`error_data` only) — where it happened: `file:line` while loading, or `script path, line N`
|
|
95
117
|
|
|
96
118
|
Some objects also have events that are triggered as part of their lifecycle. Here are some examples:
|
|
97
119
|
|
|
@@ -146,6 +168,8 @@ on_error [script] :
|
|
|
146
168
|
error_data [can] :
|
|
147
169
|
message [string] :
|
|
148
170
|
backtrace [string] :
|
|
171
|
+
kind [string] :
|
|
172
|
+
location [string] :
|
|
149
173
|
|
|
150
174
|
|
|
151
175
|
#
|
|
@@ -280,7 +304,14 @@ See also: Pathname.
|
|
|
280
304
|
|
|
281
305
|
## It
|
|
282
306
|
|
|
283
|
-
`it` is a special virtual object. `it` contains the value of the last expression or command run.
|
|
307
|
+
`it` is a special virtual object. `it` contains the value of the last expression or command run. Which commands change it follows a simple rule:
|
|
308
|
+
|
|
309
|
+
- **A question** (`check x for exists?`, `contains?`, `starts_with?`, …) puts its answer in `it`.
|
|
310
|
+
- **A command or message that works out a value** puts that value in `it` (`eval`, `put`, `show 3 + 4`) — even if it also stores it somewhere, in the object's own value (`trim`, `sub`, `format_for_html`) or in a child (`run` on an `erb`, `http_get` or `system` object also sets its `result`).
|
|
311
|
+
- **A pure action** — `list`, a bare `show`, deleting, opening a file — leaves `it` alone, so a value in `it` survives it. Running a script or a loop doesn't set `it` either, though the commands inside it do.
|
|
312
|
+
- **When something goes wrong** in a question or a command that would set `it`, the problem is reported and `it` is `false`. The same goes for a message that never gets to run — the object doesn't exist, or doesn't take that message — including `run` itself: `it` is `false`, so no stale value from an earlier line is left behind.
|
|
313
|
+
|
|
314
|
+
Each object's messages say what `it` will have (see Objects, or `help` in the app).
|
|
284
315
|
|
|
285
316
|
Get the value of an expression and store it somewhere for later use:
|
|
286
317
|
|
|
@@ -298,6 +329,23 @@ example [can] :
|
|
|
298
329
|
|
|
299
330
|
Running this script will show `7` twice. The first time will be the result of the addition. The second time will be showing the result object.
|
|
300
331
|
|
|
332
|
+
`it` is read-only. It is a result value, not an object: it has no type, and the next command that produces a result replaces it. You can read it anywhere a value is allowed (`eval it = 7`, `put it into x`, `if it then …`), but nothing can target it. Sending it a message (`check it for …`, `tell it to …`), putting a value into it, or using it with `run`, `move`, `list`, `create`, `save` or `redirect` is an error, and `it` keeps its value.
|
|
333
|
+
|
|
334
|
+
To send a message to the value in `it`, put it into an object first. Putting it into an object of a given type also says what kind of value it is:
|
|
335
|
+
|
|
336
|
+
```gloo
|
|
337
|
+
example [can] :
|
|
338
|
+
path [string] :
|
|
339
|
+
list [can] :
|
|
340
|
+
a : one
|
|
341
|
+
b : two
|
|
342
|
+
on_load [script] :
|
|
343
|
+
tell ^.list to random_child_path
|
|
344
|
+
put it into ^.path
|
|
345
|
+
check ^.path for starts_with? ( 'example.list.' )
|
|
346
|
+
show it
|
|
347
|
+
```
|
|
348
|
+
|
|
301
349
|
See also: Pathname.
|
|
302
350
|
|
|
303
351
|
## Operators
|
data/docs/objects.md
CHANGED
|
@@ -27,7 +27,7 @@ s [can] :
|
|
|
27
27
|
show it
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Sending `up` to the string converts it to uppercase, in place. Sending `size` puts the character count into `it`. There are messages for lowercasing, counting words and lines, checking prefixes/suffixes, encoding, and generating random strings (UUIDs, hex, alphanumeric) — see the in-app help for the full list.
|
|
30
|
+
Sending `up` to the string converts it to uppercase, in place. Sending `size` puts the character count into `it`. There are messages for lowercasing, counting words and lines, checking prefixes/suffixes, finding where a substring occurs (`index_of`), encoding, and generating random strings (UUIDs, hex, alphanumeric) — see the in-app help for the full list.
|
|
31
31
|
|
|
32
32
|
For yes/no messages that inspect state (`blank?`, `starts_with?`, `ends_with?`), the `check` verb reads better than `tell` — `check s.msg for starts_with? ("Hello")` — but it does the same thing (see Verbs, Tell).
|
|
33
33
|
|
|
@@ -48,6 +48,63 @@ can [can] :
|
|
|
48
48
|
|
|
49
49
|
`can.data` is itself a container holding three children; `count` puts the number of children into `it`. Because containers can nest arbitrarily, this is how gloo builds up everything from simple config blocks to entire applications.
|
|
50
50
|
|
|
51
|
+
### Getting children by position
|
|
52
|
+
|
|
53
|
+
A container keeps its children in the order they were added, so you can also reach them by position. Positions are 0-based: after `split_list`, index 0 is the child named `1`.
|
|
54
|
+
|
|
55
|
+
- `child_value_at (index)` — the value of the child at that position.
|
|
56
|
+
- `child_path_at (index)` — the child's path from root. This works for any child, including a container.
|
|
57
|
+
- `random_child_value` — the value of a randomly chosen child.
|
|
58
|
+
- `random_child_path` — the path of a randomly chosen child.
|
|
59
|
+
|
|
60
|
+
Each puts its result into `it`. The `_value` messages are for simple children; a container child has no value of its own, so asking for one is an error. Use the path instead, and put it into an alias to reach the child's fields:
|
|
61
|
+
|
|
62
|
+
```gloo
|
|
63
|
+
books [can] :
|
|
64
|
+
list [can] :
|
|
65
|
+
a [can] :
|
|
66
|
+
title [string] : Walden
|
|
67
|
+
b [can] :
|
|
68
|
+
title [string] : Emma
|
|
69
|
+
ptr [alias] :
|
|
70
|
+
on_load [script] :
|
|
71
|
+
tell books.list to random_child_path
|
|
72
|
+
put it into books.ptr*
|
|
73
|
+
show books.ptr.title
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
An index that is out of range (including a negative one) or isn't a number, or an empty container for the `random_` messages, is an error, and `it` is `false`.
|
|
77
|
+
|
|
78
|
+
The `random_` messages pick with replacement, so two calls can give the same child. For distinct picks, pick a random index with an integer's `randomize` message, keep the indexes already used as children of another container, and check it with `child_exists` before using `child_path_at`.
|
|
79
|
+
|
|
80
|
+
### Building a numbered list
|
|
81
|
+
|
|
82
|
+
To add children one at a time — say, while walking a folder — create each one through an alias. Point the alias at the next numbered path, then create the object there, and put its value in:
|
|
83
|
+
|
|
84
|
+
```gloo
|
|
85
|
+
names [can] :
|
|
86
|
+
words [string] : red green blue
|
|
87
|
+
list [can] :
|
|
88
|
+
next [int] : 0
|
|
89
|
+
slot [alias] :
|
|
90
|
+
|
|
91
|
+
add_each [each] :
|
|
92
|
+
word [string] :
|
|
93
|
+
in [alias] : names.words
|
|
94
|
+
do [script] :
|
|
95
|
+
tell names.list to count
|
|
96
|
+
put it + 1 into names.next
|
|
97
|
+
put 'names.list.' + names.next into names.slot*
|
|
98
|
+
create names.slot* as string
|
|
99
|
+
put ^.word into names.slot
|
|
100
|
+
|
|
101
|
+
on_load [script] :
|
|
102
|
+
tell names.add_each to run
|
|
103
|
+
tell names.list to show_key_value_table
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The children are named `1`, `2`, `3`, the same way `split_list` names them, so `child_value_at ( 0 )` is the child named `1`. The `*` after the alias refers to the alias itself rather than what it points to (see `alias` in the in-app help), so `put … into names.slot*` changes where it points and `create names.slot*` creates the object there.
|
|
107
|
+
|
|
51
108
|
## Integer
|
|
52
109
|
|
|
53
110
|
An integer holds a numeric value and responds to a handful of convenience messages:
|
data/docs/operators.md
CHANGED
|
@@ -5,6 +5,7 @@ Gloo operators can be used to do basic math and to compare values.
|
|
|
5
5
|
**Contents**
|
|
6
6
|
|
|
7
7
|
- Math Operators
|
|
8
|
+
- Joining Values
|
|
8
9
|
- Comparison Operators
|
|
9
10
|
- Example
|
|
10
11
|
|
|
@@ -19,6 +20,21 @@ These are the gloo math operators:
|
|
|
19
20
|
/ division
|
|
20
21
|
```
|
|
21
22
|
|
|
23
|
+
An expression is worked out strictly left to right. There's no precedence and no grouping with parentheses, so `2 + 3 * 4` is `20` (`2 + 3` first, then `* 4`), not `14`. To work out one part first, put it into an object on its own line, then use that object: `put 3 * 4 into x`, then `eval 2 + x`.
|
|
24
|
+
|
|
25
|
+
## Joining Values
|
|
26
|
+
|
|
27
|
+
`+` also joins strings: `"hello" + " world"` is `hello world`. When two values sit side by side with no operator between them, gloo joins them with `+` too.
|
|
28
|
+
|
|
29
|
+
`and` is another way to write `+`. It reads better when building a string out of several pieces:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
> put first_name and ' ' and last_name into full_name
|
|
33
|
+
> put VPM_ROOT and 'Tasks/' and file_name into path
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`and` always joins; it is not a logical (boolean) and. Because it's an operator, `and` is never looked up as an object, even if one by that name exists.
|
|
37
|
+
|
|
22
38
|
## Comparison Operators
|
|
23
39
|
|
|
24
40
|
Strings, integers, and decimal numbers can be compared.
|
data/docs/plugins.md
CHANGED
|
@@ -150,7 +150,15 @@ end
|
|
|
150
150
|
|
|
151
151
|
### Adding a Verb
|
|
152
152
|
|
|
153
|
-
A verb subclasses `Gloo::Core::Verb` and must implement `self.keyword`, `self.keyword_shortcut`, and `run`. Inside `run`, `@tokens` gives access to the parsed command line and `@engine` is the running engine (
|
|
153
|
+
A verb subclasses `Gloo::Core::Verb` and must implement `self.keyword`, `self.keyword_shortcut`, and `run`. Inside `run`, `@tokens` gives access to the parsed command line and `@engine` is the running engine (`@engine.heap.it` sets the implicit `it` result).
|
|
154
|
+
|
|
155
|
+
Report problems the same way the interpreter does (see Language, Syntax > Errors and Warnings):
|
|
156
|
+
|
|
157
|
+
- `@engine.syntax_err( msg )` — the command couldn't be understood (eg. a required expression is missing)
|
|
158
|
+
- `@engine.err( msg )` — it was understood but couldn't be done (eg. an object or file wasn't found; use `Gloo::Core::NotFound.object( path )` and friends for the message)
|
|
159
|
+
- `@engine.warn( msg )` — it was done, but probably not the way it was meant (eg. a value used as a best guess)
|
|
160
|
+
|
|
161
|
+
Don't use `@engine.log.error` or `@engine.log.warn` for these: they only write to the log, so scripts and `on_error` never see them.
|
|
154
162
|
|
|
155
163
|
The simplest possible verb — `beep`, which takes no parameters (`extensions/beep/src/beep.rb`):
|
|
156
164
|
|
data/docs/verbs.md
CHANGED
|
@@ -43,7 +43,7 @@ tell {path.to.object} to {message}
|
|
|
43
43
|
> tell the.container to count
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`check` is the same verb under another name — it sends a message exactly the way `tell` does. The two spellings exist so code reads like natural communication: use `tell` to trigger an action (`up`, `run`, `unload`), and `check` to investigate state with a yes/no question (`blank?`, `contains?`, `starts_with?`). The answer to a `check` lands in `it`, so it pairs naturally with `if` / `unless`.
|
|
46
|
+
`check` is the same verb under another name — it sends a message exactly the way `tell` does. The two spellings exist so code reads like natural communication: use `tell` to trigger an action (`up`, `run`, `unload`), and `check` to investigate state with a yes/no question (`blank?`, `contains?`, `starts_with?`). The answer to a `check` lands in `it`, so it pairs naturally with `if` / `unless`. `it` itself can't be sent a message (`check it for …` is an error); put it into an object first — see Language, Syntax > It.
|
|
47
47
|
|
|
48
48
|
```gloo
|
|
49
49
|
> tell my.str to up
|
|
@@ -75,6 +75,8 @@ put {expression} into {dst.path}
|
|
|
75
75
|
|
|
76
76
|
`it` also picks up the result of the evaluation, same as with other verbs — see It.
|
|
77
77
|
|
|
78
|
+
If the destination doesn't exist, or the expression can't be worked out (eg. it uses an object that doesn't exist), `put` reports an error, leaves the destination unchanged, and sets `it` to `false`. A value that doesn't fit the destination's type is used as a best guess, with a warning: `put 'x' into x` (an integer) gives `0` and warns `'x' is not an integer; using 0.` (see Language, Syntax > Errors and Warnings).
|
|
79
|
+
|
|
78
80
|
## Load & Save
|
|
79
81
|
|
|
80
82
|
`load` reads a `.gloo` file into the heap and runs its `on_load` script. Give a path relative to the project folder (no extension needed) or a full path (extension required); `*` in place of a file name loads every `.gloo` file in a folder.
|
|
@@ -85,7 +87,7 @@ put {expression} into {dst.path}
|
|
|
85
87
|
> load ~/.my_app/settings.gloo
|
|
86
88
|
```
|
|
87
89
|
|
|
88
|
-
If the name can't be resolved to a file, `load` reports `File not found
|
|
90
|
+
If the name can't be resolved to a file, `load` reports `File '{name}' was not found.` — it fires `on_error` and, when the file was named on the `gloo` command line, prints the message to stderr and exits non-zero. A bad or missing file never fails silently.
|
|
89
91
|
|
|
90
92
|
`save` writes loaded objects back to their files. With no argument it saves every open file; with an object it saves the file (or files) that object's tree came from; with `to {path}` it **extracts** — moves the object (and its descendants) out of whichever file currently owns them, into a new file, under their full dotted path.
|
|
91
93
|
|
data/lib/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
7.0
|
data/lib/VERSION_NOTES
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
7.0 - 2026.10.01
|
|
2
|
+
- Error handling (and warnings) revamp.
|
|
3
|
+
- String adds index_of message.
|
|
4
|
+
- Adds container messages to access children by index and random children.
|
|
5
|
+
- 'And' becomes an official operator, a variant of '+'.
|
|
6
|
+
- Adds protection against treating it as an object or message target.
|
|
7
|
+
- Adds warnings or errors when extra parameters are added to verbs.
|
|
8
|
+
- Some operations are now correctly setting it.
|
|
9
|
+
- Fixes duplicate space in format_for_html message.
|
|
10
|
+
- Better reporting for predictable failures.
|
|
11
|
+
- Fixes issue with exists? verb not including instances as part of any option.
|
|
12
|
+
- Create verb adds bad name check.
|
|
13
|
+
- Adds recursion for file and dir loops
|
|
14
|
+
- `each file` and `each dir` can walk a folder tree with `recursive: true`;
|
|
15
|
+
`in` takes a leading `~`, works with or without a trailing `/`, and may
|
|
16
|
+
include wildcards (`**/`). `ext` now ignores case.
|
|
17
|
+
- `each file` without `ext` no longer lists folders; add
|
|
18
|
+
`include_dirs: true` to get them back.
|
|
19
|
+
|
|
20
|
+
|
|
1
21
|
6.7.2 - 2026.09.16
|
|
2
22
|
- Fixes issue with logging errors in the dictionary.
|
|
3
23
|
|
data/lib/gloo/app/engine.rb
CHANGED
|
@@ -65,6 +65,8 @@ module Gloo
|
|
|
65
65
|
@log.debug 'starting the engine...'
|
|
66
66
|
@log.debug Gloo::App::Info.display_title
|
|
67
67
|
@mode = @args.detect_mode
|
|
68
|
+
# Test output is the test results; errors go to the log only.
|
|
69
|
+
@log.console_errors = false if @mode == Mode::TEST
|
|
68
70
|
@running = true
|
|
69
71
|
|
|
70
72
|
@dictionary = Gloo::Core::Dictionary.get( self )
|
|
@@ -161,7 +163,7 @@ module Gloo
|
|
|
161
163
|
begin
|
|
162
164
|
@lib_manager.load_lib TEST_LIB_NAME
|
|
163
165
|
TestRunner.new( self, @args.files ).run
|
|
164
|
-
rescue => ex
|
|
166
|
+
rescue StandardError, ScriptError => ex
|
|
165
167
|
handle_exception ex
|
|
166
168
|
end
|
|
167
169
|
|
|
@@ -245,7 +247,7 @@ module Gloo
|
|
|
245
247
|
|
|
246
248
|
begin
|
|
247
249
|
@parser.run @last_cmd
|
|
248
|
-
rescue => e
|
|
250
|
+
rescue StandardError, ScriptError => e
|
|
249
251
|
handle_exception e
|
|
250
252
|
end
|
|
251
253
|
end
|
|
@@ -331,20 +333,74 @@ module Gloo
|
|
|
331
333
|
# runs the on_error script (if any) unless we're already inside
|
|
332
334
|
# one, so a broken handler can't loop.
|
|
333
335
|
#
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
336
|
+
# Before the engine has started (eg. while start is still
|
|
337
|
+
# validating the command line) there's no heap to hold the
|
|
338
|
+
# error and nothing to handle it, so it is only logged.
|
|
339
|
+
#
|
|
340
|
+
# The location (where the failing line is: a script and line, or
|
|
341
|
+
# a file and line while loading) is found automatically unless
|
|
342
|
+
# given, and is logged in front of the message.
|
|
343
|
+
#
|
|
344
|
+
def err( msg, backtrace=nil, kind: Gloo::Core::Error::RUNTIME, location: nil )
|
|
345
|
+
location ||= @exec_env&.current_location
|
|
346
|
+
@log.error( location ? "#{location}: #{msg}" : msg )
|
|
347
|
+
return unless @heap
|
|
348
|
+
|
|
349
|
+
@heap.error.set_to msg, kind, location
|
|
337
350
|
|
|
338
351
|
return if @handling_error
|
|
339
352
|
|
|
353
|
+
# The handler's own lines run as commands, which reset and clear
|
|
354
|
+
# the error state; put it back afterward so the failed command
|
|
355
|
+
# is still seen as failed.
|
|
356
|
+
error = @heap.error
|
|
357
|
+
value, count = error.value, error.error_count
|
|
340
358
|
@handling_error = true
|
|
341
359
|
begin
|
|
342
|
-
@event_manager.on_error( msg, backtrace )
|
|
360
|
+
@event_manager.on_error( msg, backtrace, kind, location )
|
|
343
361
|
ensure
|
|
344
362
|
@handling_error = false
|
|
363
|
+
error.value = value
|
|
364
|
+
error.error_count = count
|
|
365
|
+
error.kind = kind
|
|
366
|
+
error.location = location
|
|
345
367
|
end
|
|
346
368
|
end
|
|
347
369
|
|
|
370
|
+
#
|
|
371
|
+
# Report a syntax error: the command couldn't be understood
|
|
372
|
+
# (eg. an unknown verb, or a required part of it is missing).
|
|
373
|
+
#
|
|
374
|
+
def syntax_err( msg, location: nil )
|
|
375
|
+
err( msg, kind: Gloo::Core::Error::SYNTAX, location: location )
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
#
|
|
379
|
+
# Report a warning: the command was done, but probably not the
|
|
380
|
+
# way it was meant (eg. a value that isn't really an integer,
|
|
381
|
+
# used as a best guess). Only logged, with the location in front
|
|
382
|
+
# of the message: it doesn't set the error or run on_error.
|
|
383
|
+
#
|
|
384
|
+
def warn( msg )
|
|
385
|
+
return if @warnings_off
|
|
386
|
+
|
|
387
|
+
location = @exec_env&.current_location
|
|
388
|
+
@log.warn( location ? "#{location}: #{msg}" : msg )
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
#
|
|
392
|
+
# Run the block without logging warnings, for internal work that
|
|
393
|
+
# repeats what was already warned about (eg. re-reading a value
|
|
394
|
+
# just to compare it).
|
|
395
|
+
#
|
|
396
|
+
def without_warnings
|
|
397
|
+
was_off = @warnings_off
|
|
398
|
+
@warnings_off = true
|
|
399
|
+
yield
|
|
400
|
+
ensure
|
|
401
|
+
@warnings_off = was_off
|
|
402
|
+
end
|
|
403
|
+
|
|
348
404
|
#
|
|
349
405
|
# Log an exception.
|
|
350
406
|
# This function does not log the full backtrace, but
|
|
@@ -352,7 +408,7 @@ module Gloo
|
|
|
352
408
|
#
|
|
353
409
|
def log_exception ex
|
|
354
410
|
backtrace = format_backtrace( ex )
|
|
355
|
-
@log.
|
|
411
|
+
@log.backtrace backtrace
|
|
356
412
|
|
|
357
413
|
err( ex.message, backtrace)
|
|
358
414
|
end
|
|
@@ -366,7 +422,7 @@ module Gloo
|
|
|
366
422
|
def handle_exception ex
|
|
367
423
|
backtrace = format_backtrace( ex )
|
|
368
424
|
@log.error ex.message
|
|
369
|
-
@log.
|
|
425
|
+
@log.backtrace backtrace
|
|
370
426
|
|
|
371
427
|
return if @handling_exception
|
|
372
428
|
|
data/lib/gloo/app/log.rb
CHANGED
|
@@ -23,6 +23,16 @@ module Gloo
|
|
|
23
23
|
ERROR_FILE = 'error.log'.freeze
|
|
24
24
|
|
|
25
25
|
attr_accessor :quiet
|
|
26
|
+
|
|
27
|
+
# When false, errors and warnings go to the log files only, not
|
|
28
|
+
# to the console (eg. while running tests, where the output is
|
|
29
|
+
# the test results).
|
|
30
|
+
attr_accessor :console_errors
|
|
31
|
+
|
|
32
|
+
# How many errors and warnings have been logged (since the log was
|
|
33
|
+
# created, or since reset_counts). A test runner uses these to
|
|
34
|
+
# summarize what was logged during the run.
|
|
35
|
+
attr_reader :error_count, :warning_count
|
|
26
36
|
attr_reader :logger
|
|
27
37
|
|
|
28
38
|
# ---------------------------------------------------------------------
|
|
@@ -37,6 +47,8 @@ module Gloo
|
|
|
37
47
|
def initialize( engine, quiet = true )
|
|
38
48
|
@engine = engine
|
|
39
49
|
@quiet = quiet
|
|
50
|
+
@console_errors = true
|
|
51
|
+
reset_counts
|
|
40
52
|
@debug = engine.settings.debug
|
|
41
53
|
@theme = engine.theme
|
|
42
54
|
|
|
@@ -159,9 +171,10 @@ module Gloo
|
|
|
159
171
|
# Also write to the console unless quiet.
|
|
160
172
|
#
|
|
161
173
|
def warn( msg )
|
|
174
|
+
@warning_count += 1
|
|
162
175
|
@logger.warn msg
|
|
163
176
|
@error.warn msg
|
|
164
|
-
puts @theme.warn( msg )
|
|
177
|
+
puts @theme.warn( msg ) if errors_to_console?
|
|
165
178
|
end
|
|
166
179
|
|
|
167
180
|
#
|
|
@@ -170,20 +183,48 @@ module Gloo
|
|
|
170
183
|
# Also write to the console (on stderr) unless quiet.
|
|
171
184
|
#
|
|
172
185
|
def error( msg, ex = nil, engine = nil )
|
|
186
|
+
@error_count += 1
|
|
173
187
|
engine&.heap&.error&.set_to( msg ) if engine
|
|
174
188
|
@logger.error msg
|
|
175
189
|
@error.error msg
|
|
176
190
|
if ex
|
|
177
191
|
@error.error ex.message
|
|
178
192
|
@error.error ex.backtrace
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
if errors_to_console?
|
|
194
|
+
$stderr.puts @theme.error( msg )
|
|
195
|
+
$stderr.puts @theme.error( ex.message )
|
|
196
|
+
$stderr.puts ex.backtrace
|
|
197
|
+
end
|
|
198
|
+
elsif errors_to_console?
|
|
199
|
+
$stderr.puts @theme.error( msg )
|
|
184
200
|
end
|
|
185
201
|
end
|
|
186
202
|
|
|
203
|
+
#
|
|
204
|
+
# Write a backtrace for an error that was (or is about to be)
|
|
205
|
+
# logged. It's part of that error, so it isn't counted again.
|
|
206
|
+
#
|
|
207
|
+
def backtrace( text )
|
|
208
|
+
@logger.error text
|
|
209
|
+
@error.error text
|
|
210
|
+
$stderr.puts @theme.error( text ) if errors_to_console?
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
#
|
|
214
|
+
# Start counting errors and warnings from zero.
|
|
215
|
+
#
|
|
216
|
+
def reset_counts
|
|
217
|
+
@error_count = 0
|
|
218
|
+
@warning_count = 0
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
#
|
|
222
|
+
# Should errors and warnings also be written to the console?
|
|
223
|
+
#
|
|
224
|
+
def errors_to_console?
|
|
225
|
+
return !@quiet && @console_errors
|
|
226
|
+
end
|
|
227
|
+
|
|
187
228
|
end
|
|
188
229
|
end
|
|
189
230
|
end
|