gloo 6.4.0 → 6.5.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/docs/application.md +9 -0
- data/docs/language_objects.md +40 -0
- data/docs/objects.md +1 -1
- data/docs/verbs.md +9 -0
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +11 -0
- data/lib/gloo/app/engine.rb +5 -1
- data/lib/gloo/app/log.rb +5 -5
- data/lib/gloo/app/settings.rb +4 -1
- data/lib/gloo/core/obj.rb +21 -1
- data/lib/gloo/persist/file_loader.rb +7 -0
- data/lib/gloo/persist/persist_man.rb +35 -3
- data/lib/gloo/persist/source/obj_node.rb +41 -0
- data/lib/gloo/verbs/list.rb +24 -2
- data/lib/gloo/verbs/load.rb +13 -2
- data/test.gloo/ctrl/each.test.gloo +28 -30
- data/test.gloo/dt/date.test.gloo +69 -71
- data/test.gloo/dt/datetime.test.gloo +55 -57
- data/test.gloo/dt/time.test.gloo +70 -72
- data/test.gloo/lang/continuation.test.gloo +25 -27
- data/test.gloo/lang/convert.test.gloo +50 -52
- data/test.gloo/lang/dt_comparisons.test.gloo +87 -89
- data/test.gloo/lang/exceptions.test.gloo +42 -44
- data/test.gloo/lang/gloo_sys.test.gloo +100 -102
- data/test.gloo/lang/here.test.gloo +32 -34
- data/test.gloo/lang/it.test.gloo +27 -29
- data/test.gloo/lang/literal.test.gloo +55 -57
- data/test.gloo/lang/load_lib_directive.test.gloo +18 -20
- data/test.gloo/lang/naming.test.gloo +28 -30
- data/test.gloo/lang/ops.test.gloo +53 -55
- data/test.gloo/lang/shorthand.test.gloo +15 -17
- data/test.gloo/math/add.test.gloo +40 -42
- data/test.gloo/math/div.test.gloo +23 -25
- data/test.gloo/math/mult.test.gloo +23 -25
- data/test.gloo/math/sub.test.gloo +23 -25
- data/test.gloo/objs/alias.test.gloo +15 -17
- data/test.gloo/objs/bool.test.gloo +54 -56
- data/test.gloo/objs/can.test.gloo +33 -35
- data/test.gloo/objs/cipher.test.gloo +39 -41
- data/test.gloo/objs/decimal.test.gloo +27 -29
- data/test.gloo/objs/erb.test.gloo +47 -49
- data/test.gloo/objs/file.test.gloo +68 -70
- data/test.gloo/objs/function.test.gloo +37 -39
- data/test.gloo/objs/int.test.gloo +54 -56
- data/test.gloo/objs/json.test.gloo +56 -58
- data/test.gloo/objs/obj.test.gloo +68 -48
- data/test.gloo/objs/outline.test.gloo +48 -50
- data/test.gloo/objs/password.test.gloo +34 -36
- data/test.gloo/objs/repeat.test.gloo +42 -44
- data/test.gloo/objs/script.test.gloo +13 -15
- data/test.gloo/objs/string.test.gloo +82 -84
- data/test.gloo/objs/text.test.gloo +90 -92
- data/test.gloo/objs/untyped.test.gloo +36 -38
- data/test.gloo/objs/uri.test.gloo +37 -39
- data/test.gloo/verbs/break.test.gloo +12 -14
- data/test.gloo/verbs/check.test.gloo +22 -24
- data/test.gloo/verbs/context.test.gloo +11 -13
- data/test.gloo/verbs/create.test.gloo +7 -9
- data/test.gloo/verbs/eval.test.gloo +10 -12
- data/test.gloo/verbs/exists.test.gloo +43 -45
- data/test.gloo/verbs/if.test.gloo +8 -10
- data/test.gloo/verbs/invoke.test.gloo +73 -75
- data/test.gloo/verbs/list.test.gloo +21 -23
- data/test.gloo/verbs/load.test.gloo +27 -21
- data/test.gloo/verbs/log.test.gloo +7 -9
- data/test.gloo/verbs/move.test.gloo +12 -14
- data/test.gloo/verbs/put.test.gloo +10 -12
- data/test.gloo/verbs/reload.test.gloo +30 -32
- data/test.gloo/verbs/run.test.gloo +8 -10
- data/test.gloo/verbs/save.test.gloo +90 -94
- data/test.gloo/verbs/show.test.gloo +26 -28
- data/test.gloo/verbs/tell.test.gloo +8 -10
- data/test.gloo/verbs/throw.test.gloo +31 -33
- data/test.gloo/verbs/unless.test.gloo +13 -15
- data/test.gloo/verbs/unload.test.gloo +10 -12
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19bd707a24eb9bbd3e2759cdb2ae244d2dafc28f40c0f4046c856152e343261c
|
|
4
|
+
data.tar.gz: fd3fd34b664abb32a464d3745de0ede24e136f2b2a7f375809170312bbec51a1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f21efa7866b7eeb455fb4e7a8c64465bcd41a498d6129f04220c69eb814178f92775aeb96ef0e2a972f6df481d4603acf6e3de7c728e6508262347012cfb20cc
|
|
7
|
+
data.tar.gz: 956306465a135039e185104bb81cf504af379265814b5a02ba4637c3a6412463ce655aa48bae66e1f3315fd8c90925899c2ca0afda9fbf6a795c66b35e339967
|
data/docs/application.md
CHANGED
|
@@ -30,6 +30,8 @@ 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: {name}` to stderr and exits with a non-zero status. Any other load or run error also exits non-zero, so a shell script calling gloo can tell a failed run from a clean one.
|
|
34
|
+
|
|
33
35
|
When specifying a file there are a couple ways to reference the gloo file to open:
|
|
34
36
|
|
|
35
37
|
- By Path
|
|
@@ -101,6 +103,13 @@ gloo:
|
|
|
101
103
|
#
|
|
102
104
|
list_levels: 3
|
|
103
105
|
|
|
106
|
+
#
|
|
107
|
+
# When listing the object tree, also show each object's doc (the
|
|
108
|
+
# comment declared immediately above it in its source file), if it
|
|
109
|
+
# has one.
|
|
110
|
+
#
|
|
111
|
+
list_docs: false
|
|
112
|
+
|
|
104
113
|
#
|
|
105
114
|
# Show debug statements in the log?
|
|
106
115
|
#
|
data/docs/language_objects.md
CHANGED
|
@@ -70,6 +70,46 @@ page.core.users.list [container] :
|
|
|
70
70
|
- Indented lines below a shorthand declaration are children of the last
|
|
71
71
|
segment (`list` above), not of any prefix container.
|
|
72
72
|
|
|
73
|
+
## Documenting an Object
|
|
74
|
+
|
|
75
|
+
A contiguous run of whole-line comments immediately above a declaration,
|
|
76
|
+
at the same indent and with no blank line in between, is that object's
|
|
77
|
+
**doc** — read it back at run time with `tell {obj} to doc` or
|
|
78
|
+
`check {obj} for doc` (the cleaned text lands in `it`, like any other
|
|
79
|
+
value-returning message):
|
|
80
|
+
|
|
81
|
+
```gloo
|
|
82
|
+
#
|
|
83
|
+
# The user's display name. Empty until they set it in preferences.
|
|
84
|
+
#
|
|
85
|
+
name [string] :
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
```gloo
|
|
89
|
+
> check name for doc
|
|
90
|
+
> show it
|
|
91
|
+
|
|
92
|
+
The user's display name. Empty until they set it in preferences.
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Each line's own leading whitespace and `#` marker (plus one space after
|
|
97
|
+
it, if there is one) are stripped, and the result is dedented to its
|
|
98
|
+
shallowest line — but otherwise the full block is kept exactly as
|
|
99
|
+
written, blank `#` lines above and below the text included. An object
|
|
100
|
+
with no leading comment — including anything created at run time rather
|
|
101
|
+
than loaded from a file — has a blank doc (`''`).
|
|
102
|
+
|
|
103
|
+
A comment separated from the declaration by a blank line, or at a
|
|
104
|
+
different indent, is not associated with it — it's kept as its own
|
|
105
|
+
floating comment in the file instead. When a name is declared in more
|
|
106
|
+
than one loaded file, the first non-empty doc wins (same rule as the
|
|
107
|
+
first value).
|
|
108
|
+
|
|
109
|
+
`list` can show every documented object in a listed tree: turn on the
|
|
110
|
+
`list_docs` setting (off by default) — see `list` and `settings` in the
|
|
111
|
+
in-app help.
|
|
112
|
+
|
|
73
113
|
## Keywords
|
|
74
114
|
|
|
75
115
|
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/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?`, `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.
|
|
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. 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/verbs.md
CHANGED
|
@@ -51,6 +51,13 @@ tell {path.to.object} to {message}
|
|
|
51
51
|
> if it then show 'it does'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
`doc` is one such message every object responds to: it returns the comment block declared immediately above it in its source file, cleaned up (see Language, Objects — Documenting an Object).
|
|
55
|
+
|
|
56
|
+
```gloo
|
|
57
|
+
> tell my.obj to doc
|
|
58
|
+
> show it
|
|
59
|
+
```
|
|
60
|
+
|
|
54
61
|
## Put
|
|
55
62
|
|
|
56
63
|
`put` evaluates an expression and stores the result in an object.
|
|
@@ -78,6 +85,8 @@ put {expression} into {dst.path}
|
|
|
78
85
|
> load ~/.my_app/settings.gloo
|
|
79
86
|
```
|
|
80
87
|
|
|
88
|
+
If the name can't be resolved to a file, `load` reports `File not found: {name}` — 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
|
+
|
|
81
90
|
`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 saves to a new file and remembers the mapping.
|
|
82
91
|
|
|
83
92
|
```gloo
|
data/lib/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
6.
|
|
1
|
+
6.5.1
|
data/lib/VERSION_NOTES
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
6.5.1 - 2026.09.11
|
|
2
|
+
- Adds doc to the load verb for recent changes.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
6.5.0 - 2026.09.11
|
|
6
|
+
- Adds option to show comments from the list verb.
|
|
7
|
+
- Adds message to get doc for an object.
|
|
8
|
+
- Report error if the file to load can't be found.
|
|
9
|
+
- Fixes issue with multisegment root level object.
|
|
10
|
+
|
|
11
|
+
|
|
1
12
|
6.4.0 - 2026.09.06
|
|
2
13
|
- Persistance refresh with updates for loading and saving
|
|
3
14
|
- Nested container shortcut syntax
|
data/lib/gloo/app/engine.rb
CHANGED
|
@@ -134,11 +134,15 @@ module Gloo
|
|
|
134
134
|
|
|
135
135
|
#
|
|
136
136
|
# Run files specified on the CLI.
|
|
137
|
-
# Then quit
|
|
137
|
+
# Then quit, with a non-zero exit code if any file failed to
|
|
138
|
+
# load (missing, unresolvable, or errored on load) so that a
|
|
139
|
+
# calling shell script can tell a bad run from a good one.
|
|
138
140
|
#
|
|
139
141
|
def run_files
|
|
140
142
|
load_files
|
|
143
|
+
failed = error?
|
|
141
144
|
quit
|
|
145
|
+
exit( 1 ) if failed
|
|
142
146
|
end
|
|
143
147
|
|
|
144
148
|
#
|
data/lib/gloo/app/log.rb
CHANGED
|
@@ -167,7 +167,7 @@ module Gloo
|
|
|
167
167
|
#
|
|
168
168
|
# Write an error message to the log and set the error
|
|
169
169
|
# in the engine's data heap.
|
|
170
|
-
# Also write to the console unless quiet.
|
|
170
|
+
# Also write to the console (on stderr) unless quiet.
|
|
171
171
|
#
|
|
172
172
|
def error( msg, ex = nil, engine = nil )
|
|
173
173
|
engine&.heap&.error&.set_to( msg ) if engine
|
|
@@ -176,11 +176,11 @@ module Gloo
|
|
|
176
176
|
if ex
|
|
177
177
|
@error.error ex.message
|
|
178
178
|
@error.error ex.backtrace
|
|
179
|
-
puts @theme.error( msg ) unless @quiet
|
|
180
|
-
puts @theme.error( ex.message ) unless @quiet
|
|
181
|
-
puts ex.backtrace unless @quiet
|
|
179
|
+
$stderr.puts @theme.error( msg ) unless @quiet
|
|
180
|
+
$stderr.puts @theme.error( ex.message ) unless @quiet
|
|
181
|
+
$stderr.puts ex.backtrace unless @quiet
|
|
182
182
|
else
|
|
183
|
-
puts @theme.error( msg ) unless @quiet
|
|
183
|
+
$stderr.puts @theme.error( msg ) unless @quiet
|
|
184
184
|
end
|
|
185
185
|
end
|
|
186
186
|
|
data/lib/gloo/app/settings.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Gloo
|
|
|
15
15
|
|
|
16
16
|
attr_reader :user_root, :log_path,
|
|
17
17
|
:config_path, :project_path, :ext_path,
|
|
18
|
-
:start_with, :list_indent, :list_levels, :tmp_path,
|
|
18
|
+
:start_with, :list_indent, :list_levels, :list_docs, :tmp_path,
|
|
19
19
|
:debug_path, :debug, :theme
|
|
20
20
|
|
|
21
21
|
#
|
|
@@ -41,6 +41,7 @@ module Gloo
|
|
|
41
41
|
puts theme.accent( ' Startup with: ' ) + theme.emphasis( @start_with.to_s )
|
|
42
42
|
puts theme.accent( ' Indent in Listing: ' ) + theme.emphasis( @list_indent.to_s )
|
|
43
43
|
puts theme.accent( ' List Levels: ' ) + theme.emphasis( @list_levels.to_s )
|
|
44
|
+
puts theme.accent( ' List Docs: ' ) + theme.emphasis( @list_docs.to_s )
|
|
44
45
|
puts theme.accent( ' Debug? ' ) + theme.emphasis( @debug.to_s )
|
|
45
46
|
puts theme.accent( ' Theme: ' ) + theme.emphasis( @theme )
|
|
46
47
|
puts theme.accent( ' Screen Lines: ' ) + theme.emphasis( Gloo::App::Settings.lines( @engine ).to_s )
|
|
@@ -158,6 +159,7 @@ module Gloo
|
|
|
158
159
|
@start_with = settings[ 'gloo' ][ 'start_with' ]
|
|
159
160
|
@list_indent = settings[ 'gloo' ][ 'list_indent' ]
|
|
160
161
|
@list_levels = settings[ 'gloo' ][ 'list_levels' ]
|
|
162
|
+
@list_docs = settings[ 'gloo' ][ 'list_docs' ] == true
|
|
161
163
|
|
|
162
164
|
@debug = settings[ 'gloo' ][ 'debug' ]
|
|
163
165
|
|
|
@@ -192,6 +194,7 @@ module Gloo
|
|
|
192
194
|
start_with:
|
|
193
195
|
list_indent: 2
|
|
194
196
|
list_levels: 3
|
|
197
|
+
list_docs: false
|
|
195
198
|
debug: false
|
|
196
199
|
theme: #{DEFAULT_THEME}
|
|
197
200
|
TEXT
|
data/lib/gloo/core/obj.rb
CHANGED
|
@@ -12,12 +12,21 @@ module Gloo
|
|
|
12
12
|
attr_accessor :value
|
|
13
13
|
attr_reader :children, :parent
|
|
14
14
|
|
|
15
|
+
# The object's documentation: the cleaned comment block declared
|
|
16
|
+
# immediately above it in its source file (Source::ObjNode#doc),
|
|
17
|
+
# copied on at load time by FileLoader. '' for an object with no
|
|
18
|
+
# such comment, or one created at run time. Loader-set only --
|
|
19
|
+
# there's no verb/message path to change it, so it can't drift
|
|
20
|
+
# from what the file actually says.
|
|
21
|
+
attr_accessor :doc
|
|
22
|
+
|
|
15
23
|
#
|
|
16
24
|
# Set up the object.
|
|
17
25
|
#
|
|
18
26
|
def initialize( engine )
|
|
19
27
|
@engine = engine
|
|
20
28
|
@value = ''
|
|
29
|
+
@doc = ''
|
|
21
30
|
@children = []
|
|
22
31
|
@parent = nil
|
|
23
32
|
end
|
|
@@ -329,7 +338,7 @@ module Gloo
|
|
|
329
338
|
# Get a list of message names that this object receives.
|
|
330
339
|
#
|
|
331
340
|
def self.messages
|
|
332
|
-
return %w[save reload unload blank? contains? responds_to?]
|
|
341
|
+
return %w[save reload unload blank? contains? responds_to? doc]
|
|
333
342
|
end
|
|
334
343
|
|
|
335
344
|
#
|
|
@@ -445,6 +454,17 @@ module Gloo
|
|
|
445
454
|
end
|
|
446
455
|
end
|
|
447
456
|
|
|
457
|
+
#
|
|
458
|
+
# Get this object's documentation -- the comment block declared
|
|
459
|
+
# immediately above it in its source file, cleaned up. Puts the
|
|
460
|
+
# result into 'it' rather than printing, same as other
|
|
461
|
+
# value-returning messages (count, down, blank?, ...).
|
|
462
|
+
#
|
|
463
|
+
def msg_doc
|
|
464
|
+
@engine.heap.it.set_to self.doc
|
|
465
|
+
return self.doc
|
|
466
|
+
end
|
|
467
|
+
|
|
448
468
|
|
|
449
469
|
# ---------------------------------------------------------------------
|
|
450
470
|
# Render
|
|
@@ -226,6 +226,13 @@ module Gloo
|
|
|
226
226
|
|
|
227
227
|
node = build_obj_node( leading_ws( line ), name, type, value, style )
|
|
228
228
|
node.leading_doc = @comments.take_leading_doc( line_tabs, @indent_stack.node.children )
|
|
229
|
+
# First non-empty doc wins, same as "first value wins" for a
|
|
230
|
+
# name re-declared across files -- @last is the same object
|
|
231
|
+
# across re-declarations (the factory returns the existing
|
|
232
|
+
# one), so a later, comment-less re-declaration doesn't blank
|
|
233
|
+
# out an earlier file's doc. @last can be nil for an unknown
|
|
234
|
+
# type (factory.create logs a warning and returns nil).
|
|
235
|
+
@last.doc = node.doc if @last && @last.doc.to_s.strip.empty?
|
|
229
236
|
@indent_stack.node.children << node
|
|
230
237
|
@last_node = node
|
|
231
238
|
@obj ||= @ledger.roots.last || @last
|
|
@@ -15,6 +15,7 @@ module Gloo
|
|
|
15
15
|
OBJ_NOT_FOUND_ERR = 'Could not resolve object to save: '.freeze
|
|
16
16
|
PATH_EXISTS_ERR = 'Will not overwrite a file not already saved there: '.freeze
|
|
17
17
|
RELOAD_DIRTY_WARNING = 'Reloading will discard unsaved changes in: '.freeze
|
|
18
|
+
FILE_NOT_FOUND_ERR = 'File not found: '.freeze
|
|
18
19
|
|
|
19
20
|
#
|
|
20
21
|
# Constructor for the persistence manager.
|
|
@@ -75,12 +76,20 @@ module Gloo
|
|
|
75
76
|
end
|
|
76
77
|
|
|
77
78
|
#
|
|
78
|
-
# Load the object from the file.
|
|
79
|
+
# Load the object(s) from the file (or files, for a wildcard).
|
|
80
|
+
# Returns true if at least one object was loaded, false if the
|
|
81
|
+
# name couldn't be resolved to any file or nothing loaded --
|
|
82
|
+
# a missing/unresolvable file is reported via engine.err rather
|
|
83
|
+
# than failing silently.
|
|
79
84
|
#
|
|
80
85
|
def load( name )
|
|
81
86
|
pns = get_full_path_names name
|
|
82
|
-
|
|
87
|
+
if pns.nil? || pns.empty?
|
|
88
|
+
@engine.err( "#{FILE_NOT_FOUND_ERR}#{name}" ) unless name.to_s.strip.empty?
|
|
89
|
+
return false
|
|
90
|
+
end
|
|
83
91
|
|
|
92
|
+
loaded = false
|
|
84
93
|
pns.each do |pn|
|
|
85
94
|
@engine.log.debug "Load file(s) at: #{pn}"
|
|
86
95
|
begin
|
|
@@ -90,10 +99,12 @@ module Gloo
|
|
|
90
99
|
|
|
91
100
|
@maps << fs
|
|
92
101
|
@engine.event_manager.on_load fs.obj
|
|
102
|
+
loaded = true
|
|
93
103
|
rescue => ex
|
|
94
104
|
@engine.handle_exception( ex )
|
|
95
105
|
end
|
|
96
106
|
end
|
|
107
|
+
return loaded
|
|
97
108
|
end
|
|
98
109
|
|
|
99
110
|
#
|
|
@@ -273,11 +284,32 @@ module Gloo
|
|
|
273
284
|
nodes.each do |node|
|
|
274
285
|
next unless node.is_a?( Gloo::Persist::Source::ObjNode )
|
|
275
286
|
|
|
276
|
-
|
|
287
|
+
if node.obj
|
|
288
|
+
owned[ node.obj ] = true
|
|
289
|
+
mark_shorthand_ancestors( node, owned )
|
|
290
|
+
end
|
|
277
291
|
collect_owned( node.children, owned )
|
|
278
292
|
end
|
|
279
293
|
end
|
|
280
294
|
|
|
295
|
+
#
|
|
296
|
+
# A nested-container shorthand declaration (a.b.c [type] :) only
|
|
297
|
+
# records a source node for the leaf; the intermediate containers
|
|
298
|
+
# (a, a.b) are real heap objects with no node of their own. Mark
|
|
299
|
+
# them owned too, walking up from the leaf, so another file's save
|
|
300
|
+
# doesn't see an intermediate as an orphan and adopt its whole
|
|
301
|
+
# subtree.
|
|
302
|
+
#
|
|
303
|
+
def mark_shorthand_ancestors( node, owned )
|
|
304
|
+
return unless node.name.include?( '.' )
|
|
305
|
+
|
|
306
|
+
o = node.obj.parent
|
|
307
|
+
while o && !o.root?
|
|
308
|
+
owned[ o ] = true
|
|
309
|
+
o = o.parent
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
281
313
|
#
|
|
282
314
|
# Save to a path that's already mapped to some file. If that
|
|
283
315
|
# mapping is for a different root, it's a real collision -- the
|
|
@@ -44,6 +44,47 @@ module Gloo
|
|
|
44
44
|
@children = []
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
#
|
|
48
|
+
# The cleaned-up leading_doc: each line's leading whitespace and
|
|
49
|
+
# its '#' marker (plus one following space, if any) stripped,
|
|
50
|
+
# then the result dedented to its shallowest line. The full
|
|
51
|
+
# block is kept as written, blank '#' lines at the top/bottom
|
|
52
|
+
# included -- it's a faithful reproduction of the comment, not
|
|
53
|
+
# a trimmed summary. '' when there's no leading_doc at all.
|
|
54
|
+
#
|
|
55
|
+
def doc
|
|
56
|
+
return '' unless @leading_doc
|
|
57
|
+
|
|
58
|
+
lines = @leading_doc.split( "\n" ).map { |l| strip_marker( l ) }
|
|
59
|
+
return dedent( lines ).join( "\n" )
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
#
|
|
65
|
+
# One raw comment line -> its text past the '#': leading
|
|
66
|
+
# whitespace dropped, then the '#' and at most one space after
|
|
67
|
+
# it, then trailing whitespace dropped.
|
|
68
|
+
#
|
|
69
|
+
def strip_marker( raw )
|
|
70
|
+
return raw.lstrip.sub( /\A#\x20?/, '' ).rstrip
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
# Remove the common leading whitespace shared by every non-blank
|
|
75
|
+
# line, so a comment block indented for readability (eg. a
|
|
76
|
+
# bullet list within it) keeps its relative indentation.
|
|
77
|
+
#
|
|
78
|
+
def dedent( lines )
|
|
79
|
+
non_blank = lines.reject( &:empty? )
|
|
80
|
+
return lines if non_blank.empty?
|
|
81
|
+
|
|
82
|
+
n = non_blank.map { |l| l[ /\A */ ].length }.min
|
|
83
|
+
return lines if n.zero?
|
|
84
|
+
|
|
85
|
+
return lines.map { |l| l.empty? ? l : l[ n.. ] }
|
|
86
|
+
end
|
|
87
|
+
|
|
47
88
|
end
|
|
48
89
|
end
|
|
49
90
|
end
|
data/lib/gloo/verbs/list.rb
CHANGED
|
@@ -74,6 +74,7 @@ module Gloo
|
|
|
74
74
|
#
|
|
75
75
|
def show_obj( obj, indent = ' ' )
|
|
76
76
|
theme = @engine.theme
|
|
77
|
+
show_doc( obj, indent ) if @engine.settings.list_docs
|
|
77
78
|
if obj.multiline_value? && obj.value_is_array?
|
|
78
79
|
str = theme.emphasis( "#{indent}#{obj.name}" )
|
|
79
80
|
str << theme.accent( " [#{obj.type_display}] : " )
|
|
@@ -89,6 +90,23 @@ module Gloo
|
|
|
89
90
|
end
|
|
90
91
|
end
|
|
91
92
|
|
|
93
|
+
#
|
|
94
|
+
# Show the object's doc (its leading comment, cleaned up) above
|
|
95
|
+
# its listing line, one '#'-prefixed line per line of doc.
|
|
96
|
+
# Silent when the object has none.
|
|
97
|
+
#
|
|
98
|
+
def show_doc( obj, indent )
|
|
99
|
+
return if obj.doc.to_s.strip.empty?
|
|
100
|
+
|
|
101
|
+
theme = @engine.theme
|
|
102
|
+
# split( -1 ), not each_line -- a doc ending in a blank '#' line
|
|
103
|
+
# ends with "\n", and each_line silently drops that trailing
|
|
104
|
+
# empty line rather than yielding it.
|
|
105
|
+
obj.doc.split( "\n", -1 ).each do |line|
|
|
106
|
+
@engine.log.show theme.muted( "#{indent}# #{line}".rstrip )
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
92
110
|
#
|
|
93
111
|
# Determine how many levels to show.
|
|
94
112
|
#
|
|
@@ -130,12 +148,16 @@ module Gloo
|
|
|
130
148
|
'current context. When a path is provided, it will be ' \
|
|
131
149
|
'listed instead of the current context. When using context, ' \
|
|
132
150
|
'the current context will be shown, but when context has ' \
|
|
133
|
-
'not been set, the root will be shown.'
|
|
151
|
+
'not been set, the root will be shown. When the list_docs ' \
|
|
152
|
+
'setting is on, any listed object that has a doc (the ' \
|
|
153
|
+
'comment block declared immediately above it in its source ' \
|
|
154
|
+
'file) shows it too.',
|
|
134
155
|
:syntax => [ 'list {path.to.object}' ],
|
|
135
156
|
:parameters => [
|
|
136
157
|
'{path.to.object} — Optional path to object that will be listed. When no path is provided, the current context is used.'
|
|
137
158
|
],
|
|
138
|
-
:result => 'Object and children are listed out in the CLI.'
|
|
159
|
+
:result => 'Object and children are listed out in the CLI. ' \
|
|
160
|
+
'Doc lines are included when the list_docs setting is on.',
|
|
139
161
|
:errors => [
|
|
140
162
|
"#{TARGET_MISSING_ERR}{path.to.object} — The object specified that is to be listed could not be found."
|
|
141
163
|
],
|
data/lib/gloo/verbs/load.rb
CHANGED
|
@@ -122,13 +122,24 @@ module Gloo
|
|
|
122
122
|
:errors => [
|
|
123
123
|
"#{MISSING_EXPR_ERR} — No expression is provided as parameter to the verb.",
|
|
124
124
|
"#{UNKNOWN_OPT_ERR} — The reference type given isn't file, ext, or lib.",
|
|
125
|
-
"#{WRONG_NUM_ARGS_ERR} — load expects 2 or 3 arguments (the verb, an optional reference type, and the file/ext/lib name)."
|
|
125
|
+
"#{WRONG_NUM_ARGS_ERR} — load expects 2 or 3 arguments (the verb, an optional reference type, and the file/ext/lib name).",
|
|
126
|
+
'File not found: {name} — The file name given could not be ' \
|
|
127
|
+
'resolved to a gloo file (checked as a full path, and ' \
|
|
128
|
+
'relative to the project and gloo root folders).'
|
|
126
129
|
],
|
|
127
130
|
:notes => 'Several files can contribute to one container: ' \
|
|
128
131
|
'declare it in each file and add different children, and ' \
|
|
129
132
|
'they merge in the heap. If two loaded files declare the ' \
|
|
130
133
|
'same object with different values, the first one loaded ' \
|
|
131
|
-
|
|
134
|
+
"wins and a warning is logged.\n\n" \
|
|
135
|
+
'A contiguous run of whole-line comments declared ' \
|
|
136
|
+
'immediately above an object (same indent, no blank line ' \
|
|
137
|
+
"in between) becomes that object's doc, captured as the " \
|
|
138
|
+
'file loads. Read it back with tell {obj} to doc or ' \
|
|
139
|
+
'check {obj} for doc, or see it inline in the object tree ' \
|
|
140
|
+
'with list when the list_docs setting is on. When a name ' \
|
|
141
|
+
'is declared in more than one loaded file, the first ' \
|
|
142
|
+
'non-empty doc wins, same as the first value.',
|
|
132
143
|
:examples => <<~EXAMPLES.strip
|
|
133
144
|
> load my/project/file
|
|
134
145
|
> load my/app/*
|
|
@@ -2,38 +2,36 @@
|
|
|
2
2
|
# Each and repeat control object tests
|
|
3
3
|
#
|
|
4
4
|
|
|
5
|
-
tests [can] :
|
|
6
|
-
ctrl [can] :
|
|
7
|
-
each [can] :
|
|
5
|
+
tests.ctrl.each [can] :
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
count [int] : 0
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
wrd [each] :
|
|
10
|
+
word [string] :
|
|
11
|
+
IN [string] : one two three
|
|
12
|
+
do [script] :
|
|
13
|
+
eval ^^.count + 1
|
|
14
|
+
put it into ^^.count
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
rpt [repeat] :
|
|
17
|
+
times [int] : 3
|
|
18
|
+
index [int] : 0
|
|
19
|
+
do [script] :
|
|
20
|
+
eval ^^.count + 1
|
|
21
|
+
put it into ^^.count
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
each_word [test] :
|
|
24
|
+
description [string] : Count words with each word
|
|
25
|
+
on_test [script] :
|
|
26
|
+
put 0 into ^^.count
|
|
27
|
+
tell ^^.wrd to run
|
|
28
|
+
eval ^^.count = 3
|
|
29
|
+
assert "expected 3 words counted"
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
repeat_n_times [test] :
|
|
32
|
+
description [string] : Repeat a script n times
|
|
33
|
+
on_test [script] :
|
|
34
|
+
put 0 into ^^.count
|
|
35
|
+
tell ^^.rpt to run
|
|
36
|
+
eval ^^.count = 3
|
|
37
|
+
assert "expected repeat to run 3 times"
|