gloo 6.2.1 → 6.3.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/.mailmap +1 -0
- data/docs/language_syntax.md +18 -2
- data/docs/objects.md +4 -0
- data/docs/plugins.md +12 -4
- data/docs/verbs.md +8 -0
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +4 -0
- data/lib/gloo/objs/basic/untyped.rb +14 -2
- data/lib/gloo/persist/file_loader.rb +27 -1
- data/lib/gloo/verbs/check.rb +16 -4
- data/lib/gloo/verbs/create.rb +8 -3
- data/lib/gloo/verbs/eval.rb +16 -2
- data/lib/gloo/verbs/tell.rb +15 -3
- data/lib/gloo/verbs/throw.rb +29 -1
- data/test.gloo/lang/continuation.test.gloo +39 -0
- data/test.gloo/lang/convert.test.gloo +70 -0
- data/test.gloo/lang/here.test.gloo +41 -0
- data/test.gloo/lang/it.test.gloo +41 -0
- data/test.gloo/lang/literal.test.gloo +61 -0
- data/test.gloo/lang/load_lib_directive.test.gloo +33 -0
- data/test.gloo/lang/naming.test.gloo +44 -0
- data/test.gloo/objs/cipher.test.gloo +45 -0
- data/test.gloo/objs/erb.test.gloo +18 -0
- data/test.gloo/objs/file.test.gloo +77 -0
- data/test.gloo/objs/function.test.gloo +54 -0
- data/test.gloo/objs/json.test.gloo +70 -0
- data/test.gloo/objs/outline.test.gloo +72 -0
- data/test.gloo/objs/password.test.gloo +40 -0
- data/test.gloo/objs/repeat.test.gloo +48 -0
- data/test.gloo/objs/untyped.test.gloo +20 -2
- data/test.gloo/objs/uri.test.gloo +43 -0
- data/test.gloo/verbs/context.test.gloo +19 -0
- data/test.gloo/verbs/list.test.gloo +34 -0
- data/test.gloo/verbs/load.test.gloo +32 -0
- data/test.gloo/verbs/show.test.gloo +37 -0
- data/test.gloo/verbs/throw.test.gloo +44 -0
- data/test.gloo/verbs/unload.test.gloo +21 -0
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8b2250e28df1b29cef0afb4bb989a7631b421b61aff2a6a4ca7a4a99784ea6f5
|
|
4
|
+
data.tar.gz: d1aada2c93dee25e57a9a99770637ac1bf5c76c7e5064ebfd6de0a9779c041e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0d6b391cda5acf241e452eadee0533a0929622dde953eb44fd32cadfd1a1c8c469a0b820370af157eca3f5128636127d6171fc57a6002e532e03e24d03982656
|
|
7
|
+
data.tar.gz: 97806263c82dfb98af8105c19e017dde5b3d4a426c85384c2368d7b2df1b4e9f396bc9236844b7fff77e3fd264c0aa77ccb17e10fa96d5fa4a658f7e39ec2475
|
data/.mailmap
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Eric Crane <eric.crane@mac.com> <encrane@datacor.com>
|
data/docs/language_syntax.md
CHANGED
|
@@ -88,7 +88,10 @@ The following events are application and file-level events:
|
|
|
88
88
|
- `on_quit` — event triggered when gloo is quitting
|
|
89
89
|
- `on_save` — when an object is saved, this event is triggered
|
|
90
90
|
- `on_reload` — event triggered when an object receives message to reload
|
|
91
|
-
- `on_error` — event triggered when
|
|
91
|
+
- `on_error` — event triggered when gloo hits a checked error condition (a bad path, a missing object, a verb used wrongly)
|
|
92
|
+
- `on_exception` — event triggered when gloo's safety net catches an unanticipated Ruby exception (rare in normal code; use the `throw` verb to exercise it)
|
|
93
|
+
|
|
94
|
+
`on_error` and `on_exception` are independent channels — a checked 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. 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.
|
|
92
95
|
|
|
93
96
|
Some objects also have events that are triggered as part of their lifecycle. Here are some examples:
|
|
94
97
|
|
|
@@ -143,9 +146,22 @@ on_error [script] :
|
|
|
143
146
|
error_data [can] :
|
|
144
147
|
message [string] :
|
|
145
148
|
backtrace [string] :
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
#
|
|
152
|
+
# Global Exception Handler.
|
|
153
|
+
# Same shape as on_error, with a sibling exception_data container.
|
|
154
|
+
# 'throw' deliberately raises a Ruby exception to exercise this.
|
|
155
|
+
#
|
|
156
|
+
on_exception [script] :
|
|
157
|
+
tell audit_exception.write to run
|
|
158
|
+
|
|
159
|
+
exception_data [can] :
|
|
160
|
+
message [string] :
|
|
161
|
+
backtrace [string] :
|
|
146
162
|
```
|
|
147
163
|
|
|
148
|
-
See also: Load, Reload, Unload, Save, Quit.
|
|
164
|
+
See also: Load, Reload, Unload, Save, Quit, and the `throw` verb reference.
|
|
149
165
|
|
|
150
166
|
## Function Calls
|
|
151
167
|
|
data/docs/objects.md
CHANGED
|
@@ -4,6 +4,8 @@ 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?`, `reload`, `unload`) — just not the type-specific ones. 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
|
+
|
|
7
9
|
**Contents**
|
|
8
10
|
|
|
9
11
|
- String
|
|
@@ -27,6 +29,8 @@ s [can] :
|
|
|
27
29
|
|
|
28
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.
|
|
29
31
|
|
|
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
|
+
|
|
30
34
|
## Container
|
|
31
35
|
|
|
32
36
|
A container holds other objects — it's the closest thing gloo has to a folder, a hash, or a struct. Any object nested inside a container is reachable through a dotted pathname:
|
data/docs/plugins.md
CHANGED
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
|
|
10
10
|
Core Libraries extend gloo functionality, primarily by adding object types and potentially verbs.
|
|
11
11
|
|
|
12
|
-
Be sure to load a core library (or extension) prior to loading a gloo file that includes object types defined in the library.
|
|
13
|
-
|
|
14
|
-
Use the Load Verb to use an extension.
|
|
15
|
-
|
|
16
12
|
A core library ships as its own gem (`gloo-<name>`). `load lib <name>` requires the gem, installing it first via `gem install` if it isn't already present — so the explicit `gem install` step below is optional, but doing it yourself ahead of time is recommended so the install doesn't happen mid-script. Once loaded, a library's objects and verbs show up in the `help`/`?` shell exactly like built-ins:
|
|
17
13
|
|
|
18
14
|
```gloo
|
|
@@ -21,6 +17,18 @@ A core library ships as its own gem (`gloo-<name>`). `load lib <name>` requires
|
|
|
21
17
|
help> object yaml
|
|
22
18
|
```
|
|
23
19
|
|
|
20
|
+
A library's object types must be registered before any file that declares one is parsed. To use a library type in a script file, put the `load lib` at the **top of the file**, before the first object:
|
|
21
|
+
|
|
22
|
+
```gloo
|
|
23
|
+
load lib yaml
|
|
24
|
+
|
|
25
|
+
settings [can] :
|
|
26
|
+
file [yaml] : ~/.myapp/settings.yml
|
|
27
|
+
...
|
|
28
|
+
```
|
|
29
|
+
|
|
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
|
+
|
|
24
32
|
### Available Core Libraries
|
|
25
33
|
|
|
26
34
|
- **CLI** — Use the `gloo-cli` gem when building CLI applications.
|
data/docs/verbs.md
CHANGED
|
@@ -42,6 +42,14 @@ tell {path.to.object} to {message}
|
|
|
42
42
|
> tell the.container to count
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
`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
|
+
|
|
47
|
+
```gloo
|
|
48
|
+
> tell my.str to up
|
|
49
|
+
> check my.str for starts_with? ("HELLO")
|
|
50
|
+
> if it then show 'it does'
|
|
51
|
+
```
|
|
52
|
+
|
|
45
53
|
## Put
|
|
46
54
|
|
|
47
55
|
`put` evaluates an expression and stores the result in an object.
|
data/lib/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.3.0
|
data/lib/VERSION_NOTES
CHANGED
|
@@ -47,12 +47,24 @@ module Gloo
|
|
|
47
47
|
{
|
|
48
48
|
:name => KEYWORD,
|
|
49
49
|
:shortcut => KEYWORD_SHORT,
|
|
50
|
-
:description => 'An
|
|
51
|
-
'
|
|
50
|
+
:description => 'An object with no type. A type declares ' \
|
|
51
|
+
'which messages an object can receive; an untyped object ' \
|
|
52
|
+
'receives only the base object messages (blank?, ' \
|
|
53
|
+
'contains?, responds_to?, reload, unload) but can hold a ' \
|
|
54
|
+
'value of any kind. If no type is specified when an object ' \
|
|
55
|
+
'is created, it is untyped. Prefer untyped when you only ' \
|
|
56
|
+
'need to hold, compare, or show a value and do not need ' \
|
|
57
|
+
'type-specific behaviour.',
|
|
52
58
|
:examples => <<~EXAMPLES.strip
|
|
53
59
|
> create x
|
|
54
60
|
> put 1 into x
|
|
55
61
|
> put 'string' into x
|
|
62
|
+
|
|
63
|
+
#
|
|
64
|
+
# Declared with a bare colon, or with [any]:
|
|
65
|
+
#
|
|
66
|
+
slot :
|
|
67
|
+
flag [any] :
|
|
56
68
|
EXAMPLES
|
|
57
69
|
}
|
|
58
70
|
end
|
|
@@ -12,6 +12,14 @@ module Gloo
|
|
|
12
12
|
END_BLOCK = 'END'.freeze
|
|
13
13
|
SPACE_CNT = 2
|
|
14
14
|
|
|
15
|
+
# A 'load lib {name}' (or 'load ext {name}') statement at the top
|
|
16
|
+
# of a file, before the first object declaration. It makes a core
|
|
17
|
+
# library's object types available to the declarations that
|
|
18
|
+
# follow. Same syntax as the load verb, but the loader runs it
|
|
19
|
+
# before building the object tree instead of a script running it
|
|
20
|
+
# afterward.
|
|
21
|
+
LIB_DIRECTIVE = /\A(?:load|ld)\s+(?:lib|ext)\s+\S+\s*\z/i.freeze
|
|
22
|
+
|
|
15
23
|
attr_reader :obj
|
|
16
24
|
|
|
17
25
|
#
|
|
@@ -27,6 +35,7 @@ module Gloo
|
|
|
27
35
|
@exiting_multiline = false
|
|
28
36
|
@in_block = false
|
|
29
37
|
@block_value = ''
|
|
38
|
+
@body_started = false
|
|
30
39
|
@debug = false
|
|
31
40
|
end
|
|
32
41
|
|
|
@@ -51,11 +60,28 @@ module Gloo
|
|
|
51
60
|
f.each_line do |line|
|
|
52
61
|
next if skip_line? line
|
|
53
62
|
|
|
63
|
+
if !@body_started && line =~ LIB_DIRECTIVE
|
|
64
|
+
run_lib_directive line
|
|
65
|
+
next
|
|
66
|
+
end
|
|
67
|
+
@body_started = true
|
|
68
|
+
|
|
54
69
|
handle_one_line line
|
|
55
70
|
end
|
|
56
71
|
end
|
|
57
72
|
|
|
58
|
-
#
|
|
73
|
+
#
|
|
74
|
+
# Run a top-of-file 'load lib {name}' directive. It goes through
|
|
75
|
+
# the same load verb a script would use, so libraries are loaded
|
|
76
|
+
# and their object types registered before the declarations that
|
|
77
|
+
# depend on them are parsed.
|
|
78
|
+
#
|
|
79
|
+
def run_lib_directive( line )
|
|
80
|
+
@engine.log.debug "Loading file directive: #{line.strip}"
|
|
81
|
+
@engine.parser.run line.strip
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
#
|
|
59
85
|
# Join continuation lines.
|
|
60
86
|
#
|
|
61
87
|
def join_continuations( data )
|
data/lib/gloo/verbs/check.rb
CHANGED
|
@@ -60,9 +60,13 @@ module Gloo
|
|
|
60
60
|
{
|
|
61
61
|
:name => KEYWORD,
|
|
62
62
|
:shortcut => KEYWORD_SHORT,
|
|
63
|
-
:description => 'Send a message to an object to
|
|
64
|
-
'
|
|
65
|
-
'
|
|
63
|
+
:description => 'Send a message to an object to investigate ' \
|
|
64
|
+
'its state — typically a yes/no question such as blank?, ' \
|
|
65
|
+
'contains? or starts_with?. Mechanically identical to the ' \
|
|
66
|
+
'tell verb; by convention check is used to investigate ' \
|
|
67
|
+
'state while tell is used to trigger an action. Using the ' \
|
|
68
|
+
'verb that matches intent makes the code read like ' \
|
|
69
|
+
'natural communication.',
|
|
66
70
|
:syntax => [ 'check {path.to.object} for {condition_message}' ],
|
|
67
71
|
:parameters => [
|
|
68
72
|
'{path.to.object} — The object that we want to send a condition_message to.',
|
|
@@ -74,7 +78,7 @@ module Gloo
|
|
|
74
78
|
"#{UNKNOWN_MSG_ERR} — No message was specified, or the 'for' keyword is missing.",
|
|
75
79
|
'Object was not found — The target of the message was not found.'
|
|
76
80
|
],
|
|
77
|
-
:examples => <<~EXAMPLES.strip
|
|
81
|
+
:examples => <<~EXAMPLES.strip,
|
|
78
82
|
#
|
|
79
83
|
# Base object checks.
|
|
80
84
|
#
|
|
@@ -95,6 +99,14 @@ module Gloo
|
|
|
95
99
|
check obj.msg for blank?
|
|
96
100
|
show it
|
|
97
101
|
EXAMPLES
|
|
102
|
+
:notes => <<~NOTES.strip
|
|
103
|
+
check and tell send the same message the same way — the two
|
|
104
|
+
spellings exist for readability. Use check for state questions
|
|
105
|
+
(blank?, contains?, responds_to?, starts_with?) and tell for
|
|
106
|
+
action messages (up, inc, run, unload). The answer to a check
|
|
107
|
+
lands in it, so it pairs naturally with if / unless:
|
|
108
|
+
'check my.str for blank?' then 'if it then ...'.
|
|
109
|
+
NOTES
|
|
98
110
|
}
|
|
99
111
|
end
|
|
100
112
|
|
data/lib/gloo/verbs/create.rb
CHANGED
|
@@ -88,10 +88,15 @@ module Gloo
|
|
|
88
88
|
'{type} — The type of the new object. Optional; if not provided the object will be untyped.',
|
|
89
89
|
"{value} — The initial value for the new object. Optional; if not provided the object will have the default value for the type."
|
|
90
90
|
],
|
|
91
|
-
:result => "
|
|
92
|
-
"
|
|
91
|
+
:result => "If nothing exists at the path, a new object is " \
|
|
92
|
+
"created with the given (or type-default) value and added " \
|
|
93
|
+
"to the object tree, and it is set to that value. If an " \
|
|
94
|
+
"object already exists at the path it is left unchanged — " \
|
|
95
|
+
"the type and value given here are ignored — and it is set " \
|
|
96
|
+
"to the existing object's current value.",
|
|
93
97
|
:errors => [
|
|
94
|
-
"#{NO_NAME_ERR} — The name of the object was not specified and the object cannot be created."
|
|
98
|
+
"#{NO_NAME_ERR} — The name of the object was not specified and the object cannot be created.",
|
|
99
|
+
'Could not create object. Bad path: {name} — The parent container named in the path does not exist. The path is root-relative and every container above the new object must already exist.'
|
|
95
100
|
],
|
|
96
101
|
:examples => <<~EXAMPLES.strip
|
|
97
102
|
# Basic examples of creating an object from the gloo shell:
|
data/lib/gloo/verbs/eval.rb
CHANGED
|
@@ -49,19 +49,33 @@ module Gloo
|
|
|
49
49
|
{
|
|
50
50
|
:name => KEYWORD,
|
|
51
51
|
:shortcut => KEYWORD_SHORT,
|
|
52
|
-
:description => 'Evaluate an expression and put the result
|
|
52
|
+
:description => 'Evaluate an expression and put the result ' \
|
|
53
|
+
'into it, without any other action. Unlike put it does ' \
|
|
54
|
+
'not store the result in a named object, and unlike show ' \
|
|
55
|
+
'it does not print it. Primarily used in tests: assert ' \
|
|
56
|
+
'and refute check the value of it, so eval is the bridge ' \
|
|
57
|
+
'for expressions and comparisons that do not already ' \
|
|
58
|
+
'leave a boolean there.',
|
|
53
59
|
:syntax => [ 'eval {expression}' ],
|
|
54
60
|
:parameters => [
|
|
55
61
|
'{expression} — The expression that will be evaluated. The expression is optional. If not provided, eval will result in true.'
|
|
56
62
|
],
|
|
57
63
|
:result => 'The result of the expression evaluation is put ' \
|
|
58
64
|
'into it. No other action is performed.',
|
|
59
|
-
:examples => <<~EXAMPLES.strip
|
|
65
|
+
:examples => <<~EXAMPLES.strip,
|
|
60
66
|
> eval "me"
|
|
61
67
|
> eval "hello " "world"
|
|
62
68
|
> eval 132 * 23
|
|
63
69
|
> eval path.to.num = 1
|
|
64
70
|
EXAMPLES
|
|
71
|
+
:notes => <<~NOTES.strip
|
|
72
|
+
eval was added for the gloo-test framework. assert and refute
|
|
73
|
+
only look at it; the common pattern is to run a verb or
|
|
74
|
+
message, then 'eval it = {expected}' to compare the result to
|
|
75
|
+
an expected value, then assert or refute. Bare eval (or its
|
|
76
|
+
shortcut noop) sets it to true, which pairs with assert for
|
|
77
|
+
'this state should hold' checks.
|
|
78
|
+
NOTES
|
|
65
79
|
}
|
|
66
80
|
end
|
|
67
81
|
|
data/lib/gloo/verbs/tell.rb
CHANGED
|
@@ -61,8 +61,12 @@ module Gloo
|
|
|
61
61
|
{
|
|
62
62
|
:name => KEYWORD,
|
|
63
63
|
:shortcut => KEYWORD_SHORT,
|
|
64
|
-
:description => 'Send a message to an object
|
|
65
|
-
'
|
|
64
|
+
:description => 'Send a message to an object, asking it to ' \
|
|
65
|
+
'perform an action. Mechanically identical to the check ' \
|
|
66
|
+
'verb; by convention tell is used to trigger an action ' \
|
|
67
|
+
'or change state, while check is used to investigate ' \
|
|
68
|
+
'state. Using the verb that matches intent makes the ' \
|
|
69
|
+
'code read like natural communication.',
|
|
66
70
|
:syntax => [ 'tell {path.to.object} to {message}' ],
|
|
67
71
|
:parameters => [
|
|
68
72
|
'{path.to.object} — The object that we want to send a message to.',
|
|
@@ -73,12 +77,20 @@ module Gloo
|
|
|
73
77
|
"#{UNKNOWN_MSG_ERR} — No message was specified, or the `to` keyword is missing.",
|
|
74
78
|
'Object was not found — The target of the message was not found.'
|
|
75
79
|
],
|
|
76
|
-
:examples => <<~EXAMPLES.strip
|
|
80
|
+
:examples => <<~EXAMPLES.strip,
|
|
77
81
|
> tell an.obj to unload
|
|
78
82
|
> tell the.script to run
|
|
79
83
|
> tell my.str to up
|
|
80
84
|
> tell the.container to count
|
|
81
85
|
EXAMPLES
|
|
86
|
+
:notes => <<~NOTES.strip
|
|
87
|
+
tell and check send the same message the same way — the two
|
|
88
|
+
spellings exist for readability. Use tell for action messages
|
|
89
|
+
(up, inc, run, unload, randomize) and check for state questions
|
|
90
|
+
(blank?, contains?, starts_with?). Either verb will accept
|
|
91
|
+
either kind of message, but mixing them reads wrong:
|
|
92
|
+
'tell my.str for blank?' works but should be a check.
|
|
93
|
+
NOTES
|
|
82
94
|
}
|
|
83
95
|
end
|
|
84
96
|
|
data/lib/gloo/verbs/throw.rb
CHANGED
|
@@ -72,10 +72,38 @@ module Gloo
|
|
|
72
72
|
:result => "Fires the app's on_exception handler, if one is " \
|
|
73
73
|
'defined, with exception_data.message set to the thrown ' \
|
|
74
74
|
'message. Does not fire on_error.',
|
|
75
|
-
:examples => <<~EXAMPLES.strip
|
|
75
|
+
:examples => <<~EXAMPLES.strip,
|
|
76
76
|
> throw
|
|
77
77
|
> throw "custom message"
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# Execution continues with the line after the throw.
|
|
81
|
+
#
|
|
82
|
+
demo [container] :
|
|
83
|
+
on_exception [script] :
|
|
84
|
+
show 'caught: ' + ^.exception_data.message
|
|
85
|
+
exception_data [can] :
|
|
86
|
+
message [string] :
|
|
87
|
+
backtrace [string] :
|
|
88
|
+
on_load [script] :
|
|
89
|
+
throw "boom"
|
|
90
|
+
show 'still running'
|
|
78
91
|
EXAMPLES
|
|
92
|
+
:notes => <<~NOTES.strip
|
|
93
|
+
on_error and on_exception are independent channels: a checked
|
|
94
|
+
gloo error (bad path, missing object) fires on_error only; a
|
|
95
|
+
Ruby exception (from throw, or a real bug caught by gloo's
|
|
96
|
+
safety net) fires on_exception only. Neither triggers the
|
|
97
|
+
other, and in both cases the failing line is abandoned and
|
|
98
|
+
execution continues with the next line.
|
|
99
|
+
|
|
100
|
+
gloo logs every caught exception, so a throw during a script
|
|
101
|
+
run also prints the message and a truncated backtrace to the
|
|
102
|
+
console — that is the logging working, not a crash.
|
|
103
|
+
|
|
104
|
+
See gloo_sample_code/lang/exceptions.gloo for a runnable
|
|
105
|
+
walk-through.
|
|
106
|
+
NOTES
|
|
79
107
|
}
|
|
80
108
|
end
|
|
81
109
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Line continuation tests
|
|
3
|
+
#
|
|
4
|
+
# A trailing \ continues a statement on the next line.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
tests [can] :
|
|
8
|
+
lang [can] :
|
|
9
|
+
continuation [can] :
|
|
10
|
+
|
|
11
|
+
one [string] : Hello
|
|
12
|
+
two [string] : World
|
|
13
|
+
joined [string] :
|
|
14
|
+
split [string] :
|
|
15
|
+
flag [bool] : false
|
|
16
|
+
|
|
17
|
+
split_statement_matches_single_line [test] :
|
|
18
|
+
description [string] : a statement broken with \ produces the same result as one line.
|
|
19
|
+
on_test [script] :
|
|
20
|
+
put ^^.one and ' ' and ^^.two into ^^.joined
|
|
21
|
+
|
|
22
|
+
put ^^.one and \
|
|
23
|
+
' ' and \
|
|
24
|
+
^^.two into ^^.split
|
|
25
|
+
|
|
26
|
+
eval ^^.split = ^^.joined
|
|
27
|
+
assert "expected the continued statement to match the single-line one"
|
|
28
|
+
|
|
29
|
+
eval ^^.split = 'Hello World'
|
|
30
|
+
assert "expected the joined value"
|
|
31
|
+
|
|
32
|
+
continuation_in_an_if [test] :
|
|
33
|
+
description [string] : \ works inside an if statement.
|
|
34
|
+
on_test [script] :
|
|
35
|
+
put false into ^^.flag
|
|
36
|
+
if ^^.one = 'Hello' \
|
|
37
|
+
then put true into ^^.flag
|
|
38
|
+
eval ^^.flag = true
|
|
39
|
+
assert "expected the continued if to run its then branch"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Value conversion tests
|
|
3
|
+
#
|
|
4
|
+
# 'put' converts the value to the target object's type.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
tests [can] :
|
|
8
|
+
lang [can] :
|
|
9
|
+
convert [can] :
|
|
10
|
+
|
|
11
|
+
i [integer] : 0
|
|
12
|
+
d [decimal] : 0
|
|
13
|
+
s [string] : x
|
|
14
|
+
b [boolean] : false
|
|
15
|
+
|
|
16
|
+
string_to_number [test] :
|
|
17
|
+
description [string] : a string is converted to the leading number for an int or decimal target.
|
|
18
|
+
on_test [script] :
|
|
19
|
+
put '3 third time' into ^^.i
|
|
20
|
+
eval ^^.i = 3
|
|
21
|
+
assert "expected the leading integer to be parsed"
|
|
22
|
+
|
|
23
|
+
put '3.12 more' into ^^.d
|
|
24
|
+
eval ^^.d = 3.12
|
|
25
|
+
assert "expected the leading decimal to be parsed"
|
|
26
|
+
|
|
27
|
+
put 'notanumber' into ^^.i
|
|
28
|
+
eval ^^.i = 0
|
|
29
|
+
assert "expected a non-numeric string to convert to 0"
|
|
30
|
+
|
|
31
|
+
number_to_number [test] :
|
|
32
|
+
description [string] : int and decimal convert to each other; decimal to int truncates.
|
|
33
|
+
on_test [script] :
|
|
34
|
+
put 5 into ^^.d
|
|
35
|
+
eval ^^.d = 5.0
|
|
36
|
+
assert "expected the integer to widen to a decimal"
|
|
37
|
+
|
|
38
|
+
put 9.99 into ^^.i
|
|
39
|
+
eval ^^.i = 9
|
|
40
|
+
assert "expected the decimal to truncate toward zero"
|
|
41
|
+
|
|
42
|
+
anything_to_string [test] :
|
|
43
|
+
description [string] : a number or boolean put into a string becomes its text form.
|
|
44
|
+
on_test [script] :
|
|
45
|
+
put 123 into ^^.s
|
|
46
|
+
eval ^^.s = '123'
|
|
47
|
+
assert "expected the number to become a string"
|
|
48
|
+
|
|
49
|
+
put true into ^^.s
|
|
50
|
+
eval ^^.s = 'true'
|
|
51
|
+
assert "expected the boolean to become a string"
|
|
52
|
+
|
|
53
|
+
to_boolean [test] :
|
|
54
|
+
description [string] : strings (case-insensitive) and numbers convert to a boolean.
|
|
55
|
+
on_test [script] :
|
|
56
|
+
put 'false' into ^^.b
|
|
57
|
+
eval ^^.b = false
|
|
58
|
+
assert "expected 'false' to convert to false"
|
|
59
|
+
|
|
60
|
+
put 'TRUE' into ^^.b
|
|
61
|
+
eval ^^.b = true
|
|
62
|
+
assert "expected 'TRUE' to convert to true, case-insensitively"
|
|
63
|
+
|
|
64
|
+
put 0 into ^^.b
|
|
65
|
+
eval ^^.b = false
|
|
66
|
+
assert "expected 0 to convert to false"
|
|
67
|
+
|
|
68
|
+
put 42 into ^^.b
|
|
69
|
+
eval ^^.b = true
|
|
70
|
+
assert "expected a non-zero number to convert to true"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Here / parent reference tests
|
|
3
|
+
#
|
|
4
|
+
# From inside a script, ^ is the script's containing object, ^^ its
|
|
5
|
+
# parent, ^^^ its grandparent, and so on.
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
tests [can] :
|
|
9
|
+
lang [can] :
|
|
10
|
+
here [can] :
|
|
11
|
+
|
|
12
|
+
s [string] : level 0
|
|
13
|
+
|
|
14
|
+
r1 [string] :
|
|
15
|
+
r2 [string] :
|
|
16
|
+
r3 [string] :
|
|
17
|
+
|
|
18
|
+
a [can] :
|
|
19
|
+
s [string] : level 1
|
|
20
|
+
b [can] :
|
|
21
|
+
s [string] : level 2
|
|
22
|
+
c [can] :
|
|
23
|
+
s [string] : level 3
|
|
24
|
+
capture [script] :
|
|
25
|
+
put ^.s into tests.lang.here.r1
|
|
26
|
+
put ^^.s into tests.lang.here.r2
|
|
27
|
+
put ^^^.s into tests.lang.here.r3
|
|
28
|
+
|
|
29
|
+
caret_walks_up_the_tree [test] :
|
|
30
|
+
description [string] : ^, ^^, ^^^ resolve relative to the running script's location.
|
|
31
|
+
on_test [script] :
|
|
32
|
+
tell tests.lang.here.a.b.c.capture to run
|
|
33
|
+
|
|
34
|
+
eval ^^.r1 = 'level 3'
|
|
35
|
+
assert "expected ^ to be the script's own container"
|
|
36
|
+
|
|
37
|
+
eval ^^.r2 = 'level 2'
|
|
38
|
+
assert "expected ^^ to be the parent container"
|
|
39
|
+
|
|
40
|
+
eval ^^.r3 = 'level 1'
|
|
41
|
+
assert "expected ^^^ to be the grandparent container"
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#
|
|
2
|
+
# 'it' tests
|
|
3
|
+
#
|
|
4
|
+
# 'it' holds the result of the last statement that produced one.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
tests [can] :
|
|
8
|
+
lang [can] :
|
|
9
|
+
it [can] :
|
|
10
|
+
|
|
11
|
+
n [int] : 5
|
|
12
|
+
r [string] :
|
|
13
|
+
|
|
14
|
+
set_by_the_previous_statement [test] :
|
|
15
|
+
description [string] : show, eval, tell and put all leave their result in it.
|
|
16
|
+
on_test [script] :
|
|
17
|
+
show 3 + 4
|
|
18
|
+
eval it = 7
|
|
19
|
+
assert "expected show to leave the evaluated value in it"
|
|
20
|
+
|
|
21
|
+
eval 10 * 2
|
|
22
|
+
eval it = 20
|
|
23
|
+
assert "expected eval to leave its result in it"
|
|
24
|
+
|
|
25
|
+
put 'hello' into ^^.r
|
|
26
|
+
eval it = 'hello'
|
|
27
|
+
assert "expected put to leave the stored value in it"
|
|
28
|
+
|
|
29
|
+
put 5 into ^^.n
|
|
30
|
+
tell ^^.n to inc
|
|
31
|
+
eval it = 6
|
|
32
|
+
assert "expected tell to leave the message result in it"
|
|
33
|
+
|
|
34
|
+
persists_across_statements_that_produce_nothing [test] :
|
|
35
|
+
description [string] : statements like a bare show or list leave it untouched.
|
|
36
|
+
on_test [script] :
|
|
37
|
+
eval 42
|
|
38
|
+
show
|
|
39
|
+
list ^^.n
|
|
40
|
+
eval it = 42
|
|
41
|
+
assert "expected it to survive statements that don't produce a result"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Literal value tests
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
tests [can] :
|
|
6
|
+
lang [can] :
|
|
7
|
+
literal [can] :
|
|
8
|
+
|
|
9
|
+
s [string] :
|
|
10
|
+
i [integer] :
|
|
11
|
+
d [decimal] :
|
|
12
|
+
b [boolean] :
|
|
13
|
+
|
|
14
|
+
string_quote_styles [test] :
|
|
15
|
+
description [string] : single and double quotes both make a string; the other quote can appear inside.
|
|
16
|
+
on_test [script] :
|
|
17
|
+
put 'single quoted' into ^^.s
|
|
18
|
+
eval ^^.s = 'single quoted'
|
|
19
|
+
assert "expected single quotes to work"
|
|
20
|
+
|
|
21
|
+
put "double quoted" into ^^.s
|
|
22
|
+
eval ^^.s = 'double quoted'
|
|
23
|
+
assert "expected double quotes to work"
|
|
24
|
+
|
|
25
|
+
put "you're fine" into ^^.s
|
|
26
|
+
eval ^^.s = "you're fine"
|
|
27
|
+
assert "expected an apostrophe inside double quotes to be literal"
|
|
28
|
+
|
|
29
|
+
number_literals [test] :
|
|
30
|
+
description [string] : integer and decimal literals, including negatives.
|
|
31
|
+
on_test [script] :
|
|
32
|
+
put -5 into ^^.i
|
|
33
|
+
eval ^^.i = -5
|
|
34
|
+
assert "expected a negative integer literal"
|
|
35
|
+
|
|
36
|
+
put 3.14159 into ^^.d
|
|
37
|
+
eval ^^.d = 3.14159
|
|
38
|
+
assert "expected a decimal literal"
|
|
39
|
+
|
|
40
|
+
put -2.5 into ^^.d
|
|
41
|
+
eval ^^.d = -2.5
|
|
42
|
+
assert "expected a negative decimal literal"
|
|
43
|
+
|
|
44
|
+
boolean_literals_are_case_insensitive [test] :
|
|
45
|
+
description [string] : TRUE, true, False, FALSE all parse.
|
|
46
|
+
on_test [script] :
|
|
47
|
+
put TRUE into ^^.b
|
|
48
|
+
eval ^^.b = true
|
|
49
|
+
assert "expected TRUE to parse"
|
|
50
|
+
|
|
51
|
+
put true into ^^.b
|
|
52
|
+
eval ^^.b = true
|
|
53
|
+
assert "expected true to parse"
|
|
54
|
+
|
|
55
|
+
put False into ^^.b
|
|
56
|
+
eval ^^.b = false
|
|
57
|
+
assert "expected False to parse"
|
|
58
|
+
|
|
59
|
+
put FALSE into ^^.b
|
|
60
|
+
eval ^^.b = false
|
|
61
|
+
assert "expected FALSE to parse"
|