gloo 7.0.3 → 7.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 +14 -2
- data/docs/development.md +1 -1
- data/docs/language_objects.md +37 -0
- data/docs/language_syntax.md +25 -0
- data/docs/objects.md +1 -1
- data/docs/plugins.md +9 -3
- data/docs/testing.md +206 -0
- data/docs/verbs.md +13 -0
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +15 -2
- data/lib/gloo/app/engine.rb +19 -9
- data/lib/gloo/app/settings.rb +20 -3
- data/lib/gloo/core/gloo_system.rb +7 -0
- data/lib/gloo/core/obj.rb +22 -1
- data/lib/gloo/core/tokens.rb +20 -0
- data/lib/gloo/core/verb.rb +20 -0
- data/lib/gloo/objs/basic/string_msgs.rb +6 -7
- data/lib/gloo/objs/basic/untyped.rb +2 -1
- data/lib/gloo/objs/system/file_handle.rb +5 -2
- data/lib/gloo/objs/web/json.rb +7 -5
- data/lib/gloo/persist/file_loader.rb +10 -0
- data/lib/gloo/persist/file_storage.rb +8 -2
- data/lib/gloo/persist/persist_man.rb +28 -0
- data/lib/gloo/verbs/context.rb +10 -2
- data/lib/gloo/verbs/exists.rb +14 -2
- data/lib/gloo/verbs/load.rb +16 -2
- data/lib/gloo/verbs/save.rb +14 -3
- data/test.gloo/ctrl/each.test.gloo +21 -7
- data/test.gloo/lang/declaration.test.gloo +7 -2
- data/test.gloo/lang/gloo_sys.test.gloo +12 -0
- data/test.gloo/lang/naming.test.gloo +7 -2
- data/test.gloo/objs/file.test.gloo +18 -2
- data/test.gloo/objs/fixtures/source_dir_fixture.gloo +6 -0
- 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/context.test.gloo +12 -0
- data/test.gloo/verbs/exists.test.gloo +19 -0
- data/test.gloo/verbs/load.test.gloo +101 -2
- data/test.gloo/verbs/reload.test.gloo +9 -4
- data/test.gloo/verbs/save.test.gloo +41 -16
- metadata +5 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 211368f27a7bbea86aed449d545c28f05a62857e92e8a3942394261a336e597b
|
|
4
|
+
data.tar.gz: 98ea31342225ac55d1ae499727fe2e71bf1679a8c4f1523651e57ac50829a714
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2457159e21684fbdb72367753c52204de25393f74ee715622ad9879a6b228477f580440e516fee5c0d47ea6c198f8568bd109912abb2cbb01704e1e84d94d79
|
|
7
|
+
data.tar.gz: 0ba0bd0264f57f0334312c897a07b3023bd2019e835c8ec88e6e9bd827aa6af80c4a7610c1f1198958bb8948da8e2ed4981c56b725b371086df2e8950661ab07
|
data/README.md
CHANGED
|
@@ -47,7 +47,8 @@ Read in order for a guided tour of the language, top to bottom, or jump straight
|
|
|
47
47
|
8. [Operators](docs/operators.md) — math and comparison operators
|
|
48
48
|
9. [Iterators](docs/iterators.md) — each, repeat
|
|
49
49
|
10. [Plugins](docs/plugins.md) — core libraries and user extensions
|
|
50
|
-
11. [
|
|
50
|
+
11. [Testing](docs/testing.md) — writing and running gloo tests with `gloo-test`
|
|
51
|
+
12. [A Trivial Web App](docs/web_app.md) — a two-page "Hello World" built with the `gloo-web` core library
|
|
51
52
|
|
|
52
53
|
Working on gloo itself? See [Development](docs/development.md) — source layout, running the tests, and conventions.
|
|
53
54
|
|
data/docs/application.md
CHANGED
|
@@ -17,6 +17,7 @@ Gloo keeps its configuration, projects, and logs under a per-user home directory
|
|
|
17
17
|
- `gloo.gloo_config` — the configuration directory (see Configuration, below)
|
|
18
18
|
- `gloo.gloo_projects` — the projects directory (same as `gloo.app` when an app is running)
|
|
19
19
|
- `gloo.gloo_log` — the logging directory (see Logging, below)
|
|
20
|
+
- `gloo.gloo_tmp` — a folder for temporary files, such as scratch files a script or test writes and then deletes. It's `tmp` under the gloo home directory unless `tmp_path` in `gloo.yml` says otherwise (see Configuration, below), and it's created if it isn't there. Use it instead of `/tmp`, which doesn't exist on Windows.
|
|
20
21
|
|
|
21
22
|
```
|
|
22
23
|
> show gloo.gloo_home
|
|
@@ -70,6 +71,11 @@ When specifying a file there are a couple ways to reference the gloo file to ope
|
|
|
70
71
|
- Run the script in the file parameter and then quit.
|
|
71
72
|
- If a file is provided as a parameter this option does not need to be specified.
|
|
72
73
|
--test - Run in Test mode
|
|
74
|
+
- Runs the *.test.gloo files in the folders and files given
|
|
75
|
+
(the current folder if none), using the gloo-test library.
|
|
76
|
+
- Exits with status 1 if any test failed, or errors or warnings
|
|
77
|
+
were logged that no test expected; 0 otherwise.
|
|
78
|
+
- See Testing.
|
|
73
79
|
--version - Show application version
|
|
74
80
|
--help - Show the help screen
|
|
75
81
|
```
|
|
@@ -90,6 +96,12 @@ gloo:
|
|
|
90
96
|
#
|
|
91
97
|
project_path: /Users/my_user/gloo/projects
|
|
92
98
|
|
|
99
|
+
#
|
|
100
|
+
# Folder for temporary files, such as scratch files written by
|
|
101
|
+
# tests. Scripts get it with $.gloo_tmp.
|
|
102
|
+
#
|
|
103
|
+
tmp_path: /Users/my_user/gloo/tmp
|
|
104
|
+
|
|
93
105
|
#
|
|
94
106
|
# Run this script when starting up gloo.
|
|
95
107
|
# (Only if a script file is not specified.)
|
|
@@ -148,7 +160,7 @@ Debug messages are written to the log only, but other messages are also written
|
|
|
148
160
|
|
|
149
161
|
Only error and warning level messages are written to the error log.
|
|
150
162
|
|
|
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`.
|
|
163
|
+
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`. The summary at the end of a test run says how many errors and warnings were logged, and by which tests, leaving out those a test expects (`expect_errors`). See Testing.
|
|
152
164
|
|
|
153
165
|
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.
|
|
154
166
|
|
|
@@ -199,4 +211,4 @@ help> quit
|
|
|
199
211
|
|
|
200
212
|
### Narrative Docs in the Shell
|
|
201
213
|
|
|
202
|
-
This page and
|
|
214
|
+
This page and the others in `docs/` (in the interpreter's source) are the same files shown by `help> doc {name}` — there's no separate doc server or web mode. A `gloo --doc` local web server was considered and put off in favor of this: one system, no separate build/serve step.
|
data/docs/development.md
CHANGED
|
@@ -54,7 +54,7 @@ Integration tests written in gloo itself, using the `[test]` object from the `gl
|
|
|
54
54
|
gem install gloo-test gloo-yaml gloo-md
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Each `*.test.gloo` file holds `[test]` objects whose `on_test` script uses `assert` and `refute
|
|
57
|
+
Each `*.test.gloo` file holds `[test]` objects whose `on_test` script uses `assert` and `refute`. How to write them (assertions, shuffled order, `expect_errors`, `expect_fail`, scratch files and fixtures) is in Testing; this section covers only what's particular to the interpreter's own suite. A test looks like this:
|
|
58
58
|
|
|
59
59
|
```gloo
|
|
60
60
|
tests [can] :
|
data/docs/language_objects.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
- Object Naming
|
|
6
6
|
- Nested Container Shorthand
|
|
7
|
+
- Documenting an Object
|
|
8
|
+
- Where an Object Came From
|
|
7
9
|
- Keywords
|
|
8
10
|
- Literals
|
|
9
11
|
- Value Conversion
|
|
@@ -106,10 +108,45 @@ floating comment in the file instead. When a name is declared in more
|
|
|
106
108
|
than one loaded file, the first non-empty doc wins (same rule as the
|
|
107
109
|
first value).
|
|
108
110
|
|
|
111
|
+
A doc belongs to its object: when the object is moved, extracted, or
|
|
112
|
+
deleted, its doc comment goes with it (see Verbs, Load & Save).
|
|
113
|
+
|
|
109
114
|
`list` can show every documented object in a listed tree: turn on the
|
|
110
115
|
`list_docs` setting (off by default) — see `list` and `settings` in the
|
|
111
116
|
in-app help.
|
|
112
117
|
|
|
118
|
+
## Where an Object Came From
|
|
119
|
+
|
|
120
|
+
Every object can say which file it was loaded from: `check {obj} for
|
|
121
|
+
source_file` puts the file's full path in `it`, and `check {obj} for
|
|
122
|
+
source_dir` puts that file's folder (with no trailing slash). This lets a
|
|
123
|
+
script reach files stored next to it without a hard-coded path. For
|
|
124
|
+
example, a test can load a fixture that sits beside its own file:
|
|
125
|
+
|
|
126
|
+
```gloo
|
|
127
|
+
tests.my_area [can] :
|
|
128
|
+
where [string] :
|
|
129
|
+
fixture [string] : /fixtures/sample.gloo
|
|
130
|
+
|
|
131
|
+
loads_its_fixture [test] :
|
|
132
|
+
on_test [script] :
|
|
133
|
+
check ^^ for source_dir
|
|
134
|
+
put it into ^^.where
|
|
135
|
+
load ( ^^.where ^^.fixture )
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The answer comes from the nearest object, going up from the one asked,
|
|
139
|
+
that a loaded file declares. So a nested object answers with its file,
|
|
140
|
+
even when its root container (like `tests` above) is declared in many
|
|
141
|
+
files. An object created at run time inside a loaded one answers with
|
|
142
|
+
that one's file. When several files declare the same object (a namespace
|
|
143
|
+
container), the first file loaded answers.
|
|
144
|
+
|
|
145
|
+
An object that didn't come from a file (created at run time, or
|
|
146
|
+
unloaded) answers blank (`''`); that isn't an error. On a `[file]`
|
|
147
|
+
object, `source_file` is the `.gloo` file that declared it, not the path
|
|
148
|
+
it holds.
|
|
149
|
+
|
|
113
150
|
## Keywords
|
|
114
151
|
|
|
115
152
|
Gloo doesn't reserve words the way many languages do. A verb keyword like `put` or an object type name like `string` can also be used as an object name — there's no parser conflict, because verbs are only looked up as the first word of a statement, and object type names are only looked up where a type is expected (inside the `[ ]` on a declaration). Everywhere else, the word is just a pathname segment (see Object Naming, above).
|
data/docs/language_syntax.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
**Contents**
|
|
4
4
|
|
|
5
5
|
- Color
|
|
6
|
+
- Computed Arguments
|
|
6
7
|
- Errors
|
|
7
8
|
- Events
|
|
8
9
|
- Function Calls
|
|
@@ -43,6 +44,28 @@ colors [can] :
|
|
|
43
44
|
|
|
44
45
|
See also: Show.
|
|
45
46
|
|
|
47
|
+
## Computed Arguments
|
|
48
|
+
|
|
49
|
+
Some verbs take literal text rather than an object or an expression: a file name, a path, a name. Each of these can instead be an expression in parentheses. Gloo works out the expression and uses its value as the text:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
load ( $.gloo_tmp '/scratch.gloo' )
|
|
53
|
+
load lib ( lib_name )
|
|
54
|
+
save my_obj to ( $.gloo_tmp '/my_obj' )
|
|
55
|
+
context ( 'app.' section )
|
|
56
|
+
exists? object ( type_name )
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This works for `load` (a file, `ext` or `lib`), `save … to`, `context` and `exists?`. Anything not in parentheses is still taken literally, so `load common/app/*` still loads those files. A computed file name goes through the same lookup as a literal one, so wildcards, the `.gloo` extension and project-relative names work the same way.
|
|
60
|
+
|
|
61
|
+
The `+` is optional when joining text, so `( $.gloo_tmp '/scratch.gloo' )` and `( $.gloo_tmp + '/scratch.gloo' )` are the same. A `[file]` or `[string]` object holding a path works too: `load ( my.file_obj )`.
|
|
62
|
+
|
|
63
|
+
If the expression can't be worked out (for example, an object it names doesn't exist), that's reported as an error and the verb doesn't run.
|
|
64
|
+
|
|
65
|
+
For anything else, `run ~>` works out a whole command and runs it: `run ~> 'load ' $.gloo_tmp '/x.gloo'`. Prefer the parentheses where they're available; a command built this way breaks if a path has spaces in it.
|
|
66
|
+
|
|
67
|
+
See also: Load, Save, Context, Exists.
|
|
68
|
+
|
|
46
69
|
## Errors and Warnings
|
|
47
70
|
|
|
48
71
|
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.
|
|
@@ -254,6 +277,7 @@ gloo.gloo_home # Get the gloo home directory
|
|
|
254
277
|
gloo.gloo_config # Get the gloo configuration directory
|
|
255
278
|
gloo.gloo_projects # Get the gloo projects directory
|
|
256
279
|
gloo.gloo_log # Get the gloo logging directory
|
|
280
|
+
gloo.gloo_tmp # Get the gloo tmp directory, for temporary files
|
|
257
281
|
|
|
258
282
|
SCREEN
|
|
259
283
|
gloo.screen_lines # Get the number of lines on screen.
|
|
@@ -308,6 +332,7 @@ See also: Pathname.
|
|
|
308
332
|
|
|
309
333
|
- **A question** (`check x for exists?`, `contains?`, `starts_with?`, …) puts its answer in `it`.
|
|
310
334
|
- **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`).
|
|
335
|
+
- **A message that puts its result into a destination you name** puts its status in `it`: `true` when it worked, `false` when it didn't. With no destination, the result itself goes in `it`. So `tell f to read ( text )` puts the file's contents in `text` and `true` in `it`, while `tell f to read` puts the contents in `it`. The same goes for json `parse ( dst )` and string `split_list ( delim dst )`.
|
|
311
336
|
- **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
337
|
- **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
338
|
|
data/docs/objects.md
CHANGED
|
@@ -4,7 +4,7 @@ Everything in gloo is an object. Strings, numbers, containers, scripts, function
|
|
|
4
4
|
|
|
5
5
|
Gloo ships with a large set of built-in object types, and core libraries and extensions can add more. This page doesn't try to cover them all — it walks through three common ones to get a feel for how objects work. For the complete list of object types, and every message each one supports, use the in-app help: enter `help` (or `?`), then `objects` to list them all, or `object {name}` for detail on one (see Application, Help).
|
|
6
6
|
|
|
7
|
-
An object's **type declares which messages it can receive** — `up` and `trim` for a `string`, `inc` for an `integer`, `run` for a `script`. An object doesn't have to have a type: with none it is `untyped` (short `any`), declared with a bare colon (`slot :`), with `[any]`, or by `create` with no `as`. An untyped object still takes the messages every object understands (`blank?`, `contains?`, `responds_to?`, `doc`, `reload`, `unload`) — just not the type-specific ones. Of those, `blank?` asks about the object's simple value only, and `contains?` asks whether it has children: a container with children but no value of its own is blank, and contains. Untyped is the right choice when you only need to hold, compare, or show a value: a generic result slot, a config value passed straight through, or a field whose kind of value changes over its life.
|
|
7
|
+
An object's **type declares which messages it can receive** — `up` and `trim` for a `string`, `inc` for an `integer`, `run` for a `script`. An object doesn't have to have a type: with none it is `untyped` (short `any`), declared with a bare colon (`slot :`), with `[any]`, or by `create` with no `as`. An untyped object still takes the messages every object understands (`blank?`, `contains?`, `responds_to?`, `doc`, `source_dir`, `source_file`, `reload`, `unload`) — just not the type-specific ones. Of those, `blank?` asks about the object's simple value only, and `contains?` asks whether it has children: a container with children but no value of its own is blank, and contains. Untyped is the right choice when you only need to hold, compare, or show a value: a generic result slot, a config value passed straight through, or a field whose kind of value changes over its life.
|
|
8
8
|
|
|
9
9
|
**Contents**
|
|
10
10
|
|
data/docs/plugins.md
CHANGED
|
@@ -29,6 +29,12 @@ settings [can] :
|
|
|
29
29
|
|
|
30
30
|
The loader runs these top-of-file `load lib` / `load ext` lines before it builds the object tree, so declarations below them can use the library's types. Only `load lib` and `load ext` are recognized this way, and only above the first object — a `load lib` in a script still runs when that script runs, as before.
|
|
31
31
|
|
|
32
|
+
When working on a core library, `load lib` loads the installed gem, not the copy you're editing, so its tests quietly test the installed version. To test the edited source, run from the library's folder with `RUBYLIB` pointing at its `lib/`:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
RUBYLIB="$(pwd)/lib" gloo --test test/
|
|
36
|
+
```
|
|
37
|
+
|
|
32
38
|
### Available Core Libraries
|
|
33
39
|
|
|
34
40
|
- **CLI** — Use the `gloo-cli` gem when building CLI applications.
|
|
@@ -60,7 +66,7 @@ The loader runs these top-of-file `load lib` / `load ext` lines before it builds
|
|
|
60
66
|
> load lib md
|
|
61
67
|
help> object markdown
|
|
62
68
|
```
|
|
63
|
-
- **Test** — Use the `gloo-test` gem to
|
|
69
|
+
- **Test** — Use the `gloo-test` gem to write and run gloo tests. `gloo --test` loads it for you; see Testing for how to write and run tests.
|
|
64
70
|
- Library Objects: Test
|
|
65
71
|
- Library Verbs: Assert, Refute
|
|
66
72
|
- See also: Eval, It
|
|
@@ -348,7 +354,7 @@ class Stats < Gloo::Core::Obj
|
|
|
348
354
|
end
|
|
349
355
|
```
|
|
350
356
|
|
|
351
|
-
`self.messages` should call `super` and append to it — the base `Gloo::Core::Obj` already contributes messages every object receives (`reload`, `unload`, `blank?`, `contains?`, `responds_to
|
|
357
|
+
`self.messages` should call `super` and append to it — the base `Gloo::Core::Obj` already contributes messages every object receives (`save`, `reload`, `unload`, `blank?`, `contains?`, `responds_to?`, `doc`, `source_dir`, `source_file`).
|
|
352
358
|
|
|
353
359
|
Used from gloo, once loaded:
|
|
354
360
|
|
|
@@ -374,7 +380,7 @@ Add `self.doc_data` to every verb/object class (see the `beep` and `stats` examp
|
|
|
374
380
|
|
|
375
381
|
### Testing an extension
|
|
376
382
|
|
|
377
|
-
Add a Ruby unit test per verb/object class under `test/` (the same conventions as the interpreter's own tests, see Development, Tests — inherit from the project's `GlooTest`/`BaseEngineTest` base), and add a `*.test.gloo` integration test that loads the extension and exercises it end to end, e.g. `extensions/beep/test/beep.test.gloo`:
|
|
383
|
+
Add a Ruby unit test per verb/object class under `test/` (the same conventions as the interpreter's own tests, see Development, Tests — inherit from the project's `GlooTest`/`BaseEngineTest` base), and add a `*.test.gloo` integration test that loads the extension and exercises it end to end (see Testing for how to write and run gloo tests), e.g. `extensions/beep/test/beep.test.gloo`:
|
|
378
384
|
|
|
379
385
|
```gloo
|
|
380
386
|
tests [can] :
|
data/docs/testing.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
How to write and run tests for gloo code: an app, an extension or a core library. Tests are written in gloo itself, in `*.test.gloo` files, using the `[test]` object and the `assert` and `refute` verbs from the `gloo-test` core library. This page is about those gloo tests. Ruby unit tests for an extension's Ruby code are covered in Plugins, Testing an Extension; for the interpreter's own tests, see Development, Tests.
|
|
4
|
+
|
|
5
|
+
**Contents**
|
|
6
|
+
|
|
7
|
+
- Running Tests
|
|
8
|
+
- Writing a Test
|
|
9
|
+
- Assertions
|
|
10
|
+
- Test Order and Setup
|
|
11
|
+
- Errors and Warnings
|
|
12
|
+
- Tests Meant to Fail
|
|
13
|
+
- Files in Tests
|
|
14
|
+
|
|
15
|
+
## Running Tests
|
|
16
|
+
|
|
17
|
+
Install the `gloo-test` gem, then run gloo in test mode with the tests to run:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
gem install gloo-test
|
|
21
|
+
gloo --test path/to/tests/
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`--test` loads the `gloo-test` library itself, so a test file doesn't need `load lib test`. Give any mix of folders and single files. In a folder, every `*.test.gloo` file is run, including those in subfolders. With no path, gloo looks in the current folder.
|
|
25
|
+
|
|
26
|
+
While the tests run, gloo shows a `.` for each test that passes and an `x` for each one that fails. Then it lists the failures, each with the test's path, its description and the message of the assertion that failed, and ends with a summary:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
x.....
|
|
30
|
+
|
|
31
|
+
*** Failures (1) ***
|
|
32
|
+
|
|
33
|
+
tests.math.wrong_on_purpose -> A failing test, to see the output.
|
|
34
|
+
expected 2 + 2 to be 5
|
|
35
|
+
|
|
36
|
+
Tests: 6 • Passed: 5 • Failed: 1
|
|
37
|
+
Assertions: 6 • Files: 2
|
|
38
|
+
Tests finished in 0.01 seconds
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
In test mode, errors and warnings go to the log files, not the console, so the output is just the test results (see Application, Logging). The summary says when any were logged; see Errors and Warnings, below.
|
|
42
|
+
|
|
43
|
+
When the run is over, gloo exits with status 0 if it passed, and 1 if any test failed or the summary reports errors or warnings that no test expected. A missing test file or folder counts too. So a script or CI job can tell a good run from a bad one:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
gloo --test test/ && echo "all good"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Writing a Test
|
|
50
|
+
|
|
51
|
+
A test file holds one root container with `[test]` objects inside it. By convention, the root is `tests`, and each file adds its own area under it with nested container shorthand (see Language, Objects), so many test files can share the `tests` root:
|
|
52
|
+
|
|
53
|
+
```gloo
|
|
54
|
+
tests.math [can] :
|
|
55
|
+
|
|
56
|
+
total [int] : 0
|
|
57
|
+
|
|
58
|
+
adds_up [test] :
|
|
59
|
+
description [string] : Adding two numbers.
|
|
60
|
+
on_test [script] :
|
|
61
|
+
eval 2 + 2
|
|
62
|
+
put it into ^^.total
|
|
63
|
+
eval ^^.total = 4
|
|
64
|
+
assert "expected 2 + 2 to be 4"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
A `[test]` has two children:
|
|
68
|
+
|
|
69
|
+
- `description` — what the test checks. It's shown with the test's path when the test fails.
|
|
70
|
+
- `on_test` — the script that does the work and makes the assertions.
|
|
71
|
+
|
|
72
|
+
Inside `on_test`, `^` is the test and `^^` is the container that holds it, so `^^.total` is the `total` object beside the test. Objects a test works with usually sit in the container, next to the tests.
|
|
73
|
+
|
|
74
|
+
## Assertions
|
|
75
|
+
|
|
76
|
+
`assert` and `refute` look at `it`. `assert` passes if `it` is true; `refute` passes if `it` is false. Each takes an optional message, which is shown if it fails, so write it as what was expected:
|
|
77
|
+
|
|
78
|
+
```gloo
|
|
79
|
+
check ^^.name for blank?
|
|
80
|
+
refute "expected the name to be set"
|
|
81
|
+
|
|
82
|
+
eval ^^.count > 0
|
|
83
|
+
assert "expected at least one item"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`expect` is another name for `assert`, and `expect_not` for `refute`.
|
|
87
|
+
|
|
88
|
+
So a test usually puts the thing it's checking into `it` first: with `eval` for a comparison, `check` for a question (`blank?`, `contains?`, `exists?`), or a message or verb that sets `it` (see Language, Syntax, It). A test can have any number of assertions, and it fails if any one of them fails.
|
|
89
|
+
|
|
90
|
+
## Test Order and Setup
|
|
91
|
+
|
|
92
|
+
Tests run in a random order: the files are shuffled, and so are the tests within each file. A test mustn't depend on another test having run first. Each one sets up what it needs and cleans up after itself.
|
|
93
|
+
|
|
94
|
+
Between files, the heap is emptied: objects a file loads are gone before the next file runs. Within a file, the tests share the file's objects, so a test that uses one should set it at the start rather than rely on its declared value. For example, `put false into ^^.err_fired` before the step that should set it.
|
|
95
|
+
|
|
96
|
+
Setup that's the same for every test in a file, and that no test changes, can go in an `on_load` script in the file's container. It runs when the file loads, before any of its tests:
|
|
97
|
+
|
|
98
|
+
```gloo
|
|
99
|
+
tests.files [can] :
|
|
100
|
+
|
|
101
|
+
scratch [file] :
|
|
102
|
+
|
|
103
|
+
# Runs when this file loads, before any test.
|
|
104
|
+
on_load [script] :
|
|
105
|
+
put $.gloo_tmp '/my_scratch.gloo' into ^.scratch
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Errors and Warnings
|
|
109
|
+
|
|
110
|
+
The run keeps track of the errors and warnings logged by each test. If any were logged, the summary gets an extra line naming the tests that logged them:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
Tests: 4 • Passed: 4 • Failed: 0
|
|
114
|
+
Assertions: 4 • Files: 1
|
|
115
|
+
1 error logged during the run; see ~/gloo/logs/error.log
|
|
116
|
+
tests.files.missing_file_is_an_error (1 error)
|
|
117
|
+
Tests finished in 0.0 seconds
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Problems logged while a test file loads, outside any test, are counted too, as `(outside tests: …)`. A clean run has no such line. Treat one as a problem even when every test passed: something went wrong that no assertion caught.
|
|
121
|
+
|
|
122
|
+
A test that triggers an error or a warning on purpose (to check an `on_error` handler, or that bad input is reported) sets `expect_errors` to true. What it logs is then left out of the summary:
|
|
123
|
+
|
|
124
|
+
```gloo
|
|
125
|
+
tests.files [can] :
|
|
126
|
+
|
|
127
|
+
err_fired [bool] : false
|
|
128
|
+
|
|
129
|
+
on_error [script] :
|
|
130
|
+
put true into ^.err_fired
|
|
131
|
+
|
|
132
|
+
missing_file_is_an_error [test] :
|
|
133
|
+
description [string] : Loading a file that isn't there fires on_error.
|
|
134
|
+
expect_errors [bool] : true
|
|
135
|
+
on_test [script] :
|
|
136
|
+
put false into ^^.err_fired
|
|
137
|
+
load no_such_file_xyz
|
|
138
|
+
eval ^^.err_fired = true
|
|
139
|
+
assert "expected on_error to fire"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Tests Meant to Fail
|
|
143
|
+
|
|
144
|
+
A test that's meant to fail, such as one that shows an assertion really does fail when it should, sets `expect_fail` to true. It then passes if at least one of its assertions fails, and fails if none does:
|
|
145
|
+
|
|
146
|
+
```gloo
|
|
147
|
+
false_fails_an_assert [test] :
|
|
148
|
+
description [string] : assert fails when it is false.
|
|
149
|
+
expect_fail [bool] : true
|
|
150
|
+
on_test [script] :
|
|
151
|
+
eval 1 = 2
|
|
152
|
+
assert "this one is meant to fail"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`expect_fail` can't tell which assertion failed, or why, so a test can fail for the wrong reason and still pass. Keep a test like this to the one thing it checks. That's good practice for every test, but it matters most here.
|
|
156
|
+
|
|
157
|
+
## Files in Tests
|
|
158
|
+
|
|
159
|
+
**Scratch files.** A test that needs a file it writes and then deletes should put it in the gloo tmp folder, `$.gloo_tmp`, rather than `/tmp`, which doesn't exist on Windows (see Application, The Gloo Home Directory). Set the path once in `on_load`, as in Test Order and Setup, above. To load the file, put the path in parentheses so it's worked out rather than taken literally (see Language, Syntax, Computed Arguments). Unload what was loaded and delete the file at the end of the test:
|
|
160
|
+
|
|
161
|
+
```gloo
|
|
162
|
+
tell ^^.scratch to write ( ^^.source )
|
|
163
|
+
load ( ^^.scratch )
|
|
164
|
+
# ... assertions ...
|
|
165
|
+
tell demo to unload
|
|
166
|
+
tell ^^.scratch to delete
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Fixtures.** A file the tests read but never change can be kept next to the test file, for example in a `fixtures` folder. Name it something other than `*.test.gloo`, so it isn't run as a test. `check ^^ for source_dir` gives the folder of the test file (see Language, Objects, Where an Object Came From):
|
|
170
|
+
|
|
171
|
+
```gloo
|
|
172
|
+
tests.files [can] :
|
|
173
|
+
|
|
174
|
+
where [string] :
|
|
175
|
+
fixture [string] : /fixtures/sample.gloo
|
|
176
|
+
|
|
177
|
+
loads_its_fixture [test] :
|
|
178
|
+
description [string] : A fixture next to this file can be loaded.
|
|
179
|
+
on_test [script] :
|
|
180
|
+
check ^^ for source_dir
|
|
181
|
+
put it into ^^.where
|
|
182
|
+
load ( ^^.where ^^.fixture )
|
|
183
|
+
eval sample.name = 'from the fixture'
|
|
184
|
+
assert "expected the fixture to load"
|
|
185
|
+
tell sample to unload
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Broken source on purpose.** To test how gloo handles a mistake in a file, such as a missing `]` or a declaration with no name, don't put the mistake in the test file itself. It would be reported while the file loads, outside any test. Instead, keep the broken source in a `[text]` object, write it to a scratch file inside the test, and load it there. The problem then belongs to that test, which sets `expect_errors`:
|
|
189
|
+
|
|
190
|
+
```gloo
|
|
191
|
+
broken_src [text] : BEGIN
|
|
192
|
+
broken_demo [can] :
|
|
193
|
+
count [int : 3
|
|
194
|
+
END
|
|
195
|
+
|
|
196
|
+
broken_source_still_loads [test] :
|
|
197
|
+
description [string] : A type missing its ] is still read as that type.
|
|
198
|
+
expect_errors [bool] : true
|
|
199
|
+
on_test [script] :
|
|
200
|
+
tell ^^.scratch to write ( ^^.broken_src )
|
|
201
|
+
load ( ^^.scratch )
|
|
202
|
+
eval broken_demo.count = 3
|
|
203
|
+
assert "expected the value 3"
|
|
204
|
+
tell broken_demo to unload
|
|
205
|
+
tell ^^.scratch to delete
|
|
206
|
+
```
|
data/docs/verbs.md
CHANGED
|
@@ -58,6 +58,13 @@ tell {path.to.object} to {message}
|
|
|
58
58
|
> show it
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
`source_dir` and `source_file` are two more: they put the folder, or the full path, of the file the object was loaded from in `it` — blank if it didn't come from a file (see Language, Objects — Where an Object Came From).
|
|
62
|
+
|
|
63
|
+
```gloo
|
|
64
|
+
> check my.obj for source_dir
|
|
65
|
+
> show it
|
|
66
|
+
```
|
|
67
|
+
|
|
61
68
|
## Put
|
|
62
69
|
|
|
63
70
|
`put` evaluates an expression and stores the result in an object.
|
|
@@ -99,6 +106,12 @@ If the name can't be resolved to a file, `load` reports `File '{name}' was not f
|
|
|
99
106
|
|
|
100
107
|
A save is a **rewrite, not a regeneration**: comments, blank lines, and the original spacing are kept, and only the values you actually changed are re-written. A declaration you never touched comes back byte-for-byte.
|
|
101
108
|
|
|
109
|
+
What counts as a change is narrow: an object is changed when its value is set, or when it is created, moved (`move`, or extracted with `save … to`), or deleted (for example with `unload`); everything else is untouched and comes back exactly as written, wherever it lives. Adding, moving, or removing an object's children doesn't change the object itself: its own declaration stays as it was, and only the children's lines appear, move, or disappear.
|
|
110
|
+
|
|
111
|
+
An object's doc comment goes wherever the object goes: it moves with a moved or extracted object and disappears with a deleted one.
|
|
112
|
+
|
|
113
|
+
A save never loses or alters data: loading the saved files again gives back the same objects, with the same values and docs, under the same parents. The interpreter decides how a changed or new declaration is laid out; only what it says has to come back the same.
|
|
114
|
+
|
|
102
115
|
`save {obj} to {path}` doesn't just write a copy — `obj` stops being declared in its old file, wherever that was. Given `app.core.settings` declared inside `app.gloo`, `save app.core.settings to config/settings` leaves `app.gloo` without a `settings` subtree and writes `config/settings.gloo` with `app.core.settings [can] : ...` at the top level (the `app`/`core` prefix is implied via nested-container shorthand, not repeated as nested declarations) plus everything `settings` owns underneath, comments included. An object with no file of its own yet (brand new, or nested under an object that's never been saved) is simply written fresh — nothing to move.
|
|
103
116
|
|
|
104
117
|
If `obj`'s own subtree already has declarations spread across more than one file (the namespace-merge pattern below, applied *inside* the subtree being extracted, not just above it), extraction refuses rather than guessing which file's slice is authoritative — save each contributing file on its own first.
|
data/lib/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
7.
|
|
1
|
+
7.1
|
data/lib/VERSION_NOTES
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
7.1 - 2026.10.23
|
|
2
|
+
- Loading an empty gloo file is now allowed and only gives a warning.
|
|
3
|
+
- Adds configuration setting for tmp directory.
|
|
4
|
+
- load (file, lib, ext), save … to, context and exists? accept ( expression ) in place of a literal name or path. For example, load ( $.gloo_tmp '/x.gloo' ).
|
|
5
|
+
- save … to with a blank path reports the missing-path error.
|
|
6
|
+
- Updates for more consistent results in `it`.
|
|
7
|
+
- Get source file path and name for object.
|
|
8
|
+
- Adds doc for gloo tests.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
7.0.4 - 2026.10.19
|
|
12
|
+
- Documentation update.
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
7.0.3 - 2026.10.15
|
|
2
16
|
- Removes test dependency on gloo-beep.
|
|
3
17
|
|
|
@@ -165,7 +179,7 @@
|
|
|
165
179
|
|
|
166
180
|
|
|
167
181
|
5.1.0 - 2026.02.15
|
|
168
|
-
- Fixes issue with int * dec
|
|
182
|
+
- Fixes issue with int * dec
|
|
169
183
|
- Some code cleanup
|
|
170
184
|
|
|
171
185
|
|
|
@@ -392,4 +406,3 @@ adds tag helper for apple touch icon
|
|
|
392
406
|
|
|
393
407
|
2.0 - 2023.05.22
|
|
394
408
|
- Version 2 merges gloo-lang back into a single gloo gem.
|
|
395
|
-
|
data/lib/gloo/app/engine.rb
CHANGED
|
@@ -154,21 +154,31 @@ module Gloo
|
|
|
154
154
|
@args.files.each { |f| @persist_man.load( f ) }
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
-
#
|
|
157
|
+
#
|
|
158
158
|
# Run in gloo unit test mode.
|
|
159
|
+
# Then quit, with exit status 1 if the run failed (see run_tests),
|
|
160
|
+
# so a calling script or CI job can tell.
|
|
159
161
|
#
|
|
160
162
|
def run_test
|
|
161
163
|
@log.debug 'Running in Test mode…'
|
|
162
|
-
|
|
163
|
-
begin
|
|
164
|
-
@lib_manager.load_lib TEST_LIB_NAME
|
|
165
|
-
TestRunner.new( self, @args.files ).run
|
|
166
|
-
rescue StandardError, ScriptError => ex
|
|
167
|
-
handle_exception ex
|
|
168
|
-
end
|
|
169
|
-
|
|
164
|
+
passed = run_tests
|
|
170
165
|
@log.debug 'Tests complete.'
|
|
171
166
|
quit
|
|
167
|
+
exit( 1 ) unless passed
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
#
|
|
171
|
+
# Load gloo-test and run the tests given on the command line.
|
|
172
|
+
# True unless the run reports that it failed, or something went
|
|
173
|
+
# wrong running it. An older gloo-test whose run doesn't report
|
|
174
|
+
# counts as passed, as it always has.
|
|
175
|
+
#
|
|
176
|
+
def run_tests
|
|
177
|
+
@lib_manager.load_lib TEST_LIB_NAME
|
|
178
|
+
return TestRunner.new( self, @args.files ).run != false
|
|
179
|
+
rescue StandardError, ScriptError => ex
|
|
180
|
+
handle_exception ex
|
|
181
|
+
return false
|
|
172
182
|
end
|
|
173
183
|
|
|
174
184
|
#
|
data/lib/gloo/app/settings.rb
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#
|
|
6
6
|
|
|
7
7
|
require 'yaml'
|
|
8
|
+
require 'fileutils'
|
|
8
9
|
|
|
9
10
|
module Gloo
|
|
10
11
|
module App
|
|
@@ -137,9 +138,6 @@ module Gloo
|
|
|
137
138
|
@ext_path = File.join( @user_root, 'extensions' )
|
|
138
139
|
Dir.mkdir( @ext_path ) unless File.exist?( @ext_path )
|
|
139
140
|
|
|
140
|
-
@tmp_path = File.join( @user_root, 'tmp' )
|
|
141
|
-
Dir.mkdir( @tmp_path ) unless File.exist?( @tmp_path )
|
|
142
|
-
|
|
143
141
|
@debug_path = File.join( @user_root, 'debug' )
|
|
144
142
|
Dir.mkdir( @debug_path ) unless File.exist?( @debug_path )
|
|
145
143
|
end
|
|
@@ -156,6 +154,8 @@ module Gloo
|
|
|
156
154
|
@project_path = File.join( @user_root, 'projects' )
|
|
157
155
|
end
|
|
158
156
|
|
|
157
|
+
init_tmp_path( settings[ 'gloo' ][ 'tmp_path' ] )
|
|
158
|
+
|
|
159
159
|
@start_with = settings[ 'gloo' ][ 'start_with' ]
|
|
160
160
|
@list_indent = settings[ 'gloo' ][ 'list_indent' ]
|
|
161
161
|
@list_levels = settings[ 'gloo' ][ 'list_levels' ]
|
|
@@ -167,6 +167,16 @@ module Gloo
|
|
|
167
167
|
@theme = DEFAULT_THEME unless THEMES.include?( @theme )
|
|
168
168
|
end
|
|
169
169
|
|
|
170
|
+
#
|
|
171
|
+
# Set the tmp folder: the tmp_path setting if there is one (a
|
|
172
|
+
# leading ~ is expanded), or tmp under the user root. Created
|
|
173
|
+
# if it isn't there yet.
|
|
174
|
+
#
|
|
175
|
+
def init_tmp_path( path )
|
|
176
|
+
@tmp_path = path.blank? ? File.join( @user_root, 'tmp' ) : File.expand_path( path.to_s )
|
|
177
|
+
FileUtils.mkdir_p( @tmp_path ) unless File.exist?( @tmp_path )
|
|
178
|
+
end
|
|
179
|
+
|
|
170
180
|
#
|
|
171
181
|
# Get the app's required directories.
|
|
172
182
|
#
|
|
@@ -188,6 +198,7 @@ module Gloo
|
|
|
188
198
|
#
|
|
189
199
|
def get_default_settings
|
|
190
200
|
projects = File.join( @user_root, 'projects' )
|
|
201
|
+
tmp = File.join( @user_root, 'tmp' )
|
|
191
202
|
str = <<~TEXT
|
|
192
203
|
#
|
|
193
204
|
# Gloo configuration
|
|
@@ -200,6 +211,12 @@ module Gloo
|
|
|
200
211
|
#
|
|
201
212
|
project_path: #{projects}
|
|
202
213
|
|
|
214
|
+
#
|
|
215
|
+
# Folder for temporary files, such as scratch files written by
|
|
216
|
+
# tests. Scripts get it with $.gloo_tmp.
|
|
217
|
+
#
|
|
218
|
+
tmp_path: #{tmp}
|
|
219
|
+
|
|
203
220
|
#
|
|
204
221
|
# Run this script when starting up gloo.
|
|
205
222
|
# (Only if a script file is not specified.)
|
|
@@ -179,6 +179,11 @@ module Gloo
|
|
|
179
179
|
return @engine.settings.project_path
|
|
180
180
|
end
|
|
181
181
|
|
|
182
|
+
# Get the Gloo tmp directory
|
|
183
|
+
def msg_gloo_tmp
|
|
184
|
+
return @engine.settings.tmp_path
|
|
185
|
+
end
|
|
186
|
+
|
|
182
187
|
# The running app directory.
|
|
183
188
|
def msg_app
|
|
184
189
|
return @engine.settings.project_path
|
|
@@ -314,6 +319,8 @@ module Gloo
|
|
|
314
319
|
'gloo_config — Get the gloo configuration directory.',
|
|
315
320
|
'gloo_projects — Get the gloo projects directory.',
|
|
316
321
|
'gloo_log — Get the gloo logging directory.',
|
|
322
|
+
'gloo_tmp — Get the gloo tmp directory, for temporary files ' \
|
|
323
|
+
'(the tmp_path setting in gloo.yml).',
|
|
317
324
|
'screen_lines — Get the number of lines on screen.',
|
|
318
325
|
'screen_cols — Get the number of columns on screen.',
|
|
319
326
|
'platform_cpu — Get the platform CPU.',
|