gloo 6.3.0 → 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CLAUDE.md +13 -0
- data/docs/language_objects.md +38 -0
- data/docs/language_scripting.md +41 -0
- data/docs/verbs.md +27 -2
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +10 -0
- data/lib/gloo/core/obj.rb +40 -1
- data/lib/gloo/core/parser.rb +30 -1
- data/lib/gloo/core/tokens.rb +44 -18
- data/lib/gloo/expr/l_string.rb +8 -4
- data/lib/gloo/objs/basic/script.rb +28 -2
- data/lib/gloo/objs/web/json.rb +4 -1
- data/lib/gloo/persist/comment_buffer.rb +58 -0
- data/lib/gloo/persist/declaration_ledger.rb +61 -0
- data/lib/gloo/persist/disc_mech.rb +15 -2
- data/lib/gloo/persist/file_loader.rb +201 -100
- data/lib/gloo/persist/file_saver.rb +251 -6
- data/lib/gloo/persist/file_storage.rb +10 -4
- data/lib/gloo/persist/indent_stack.rb +90 -0
- data/lib/gloo/persist/line_splitter.rb +18 -3
- data/lib/gloo/persist/persist_man.rb +159 -24
- data/lib/gloo/persist/script_body_collector.rb +102 -0
- data/lib/gloo/persist/shorthand_expander.rb +51 -0
- data/lib/gloo/persist/source/blank_node.rb +27 -0
- data/lib/gloo/persist/source/comment_node.rb +27 -0
- data/lib/gloo/persist/source/directive_node.rb +26 -0
- data/lib/gloo/persist/source/obj_node.rb +50 -0
- data/lib/gloo/persist/source/source_doc.rb +41 -0
- data/lib/gloo/verbs/load.rb +5 -0
- data/lib/gloo/verbs/reload.rb +3 -0
- data/lib/gloo/verbs/save.rb +57 -10
- data/test.gloo/lang/shorthand.test.gloo +29 -0
- data/test.gloo/objs/json.test.gloo +9 -0
- data/test.gloo/objs/script.test.gloo +1 -1
- data/test.gloo/objs/text.test.gloo +22 -1
- data/test.gloo/string/str.test.gloo +15 -0
- data/test.gloo/verbs/reload.test.gloo +57 -0
- data/test.gloo/verbs/save.test.gloo +115 -0
- metadata +14 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Author:: Eric Crane (mailto:eric.crane@mac.com)
|
|
2
|
+
# Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# One object declaration in a source file, eg.
|
|
5
|
+
# name [type] : value
|
|
6
|
+
# Carries the raw text needed to reproduce the declaration unchanged,
|
|
7
|
+
# plus a back-reference to the heap object it created (or updated),
|
|
8
|
+
# so a save can tell whether the live value still matches what's on
|
|
9
|
+
# disk and, if so, leave the raw text alone.
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
module Gloo
|
|
13
|
+
module Persist
|
|
14
|
+
module Source
|
|
15
|
+
class ObjNode
|
|
16
|
+
|
|
17
|
+
# block_style is one of:
|
|
18
|
+
# :inline -- name [type] : value, all on one line
|
|
19
|
+
# :begin_end -- name [type] : BEGIN ... END
|
|
20
|
+
# :body -- name [type] :, followed by an indented,
|
|
21
|
+
# unparsed body (script commands)
|
|
22
|
+
attr_accessor :name, :raw_type, :raw_indent, :raw_tail, :block_style,
|
|
23
|
+
:raw_value, :raw_end_indent, :leading_doc, :trailing_comment, :obj
|
|
24
|
+
attr_reader :children
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Set up an object declaration node. Everything but the name
|
|
28
|
+
# and raw type is optional and commonly set afterward via the
|
|
29
|
+
# accessors above -- keeping the initializer small.
|
|
30
|
+
#
|
|
31
|
+
def initialize( name:, raw_type: )
|
|
32
|
+
@name = name
|
|
33
|
+
@raw_type = raw_type
|
|
34
|
+
@raw_indent = ''
|
|
35
|
+
@raw_tail = ''
|
|
36
|
+
@block_style = :inline
|
|
37
|
+
@raw_value = nil
|
|
38
|
+
# a begin_end block's closing END is not necessarily indented
|
|
39
|
+
# to match its declaration -- kept separately, raw.
|
|
40
|
+
@raw_end_indent = ''
|
|
41
|
+
@leading_doc = nil
|
|
42
|
+
@trailing_comment = nil
|
|
43
|
+
@obj = nil
|
|
44
|
+
@children = []
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Author:: Eric Crane (mailto:eric.crane@mac.com)
|
|
2
|
+
# Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# The concrete source model for one loaded file: an ordered list of
|
|
5
|
+
# nodes (comments, blank lines, directives, and object declarations)
|
|
6
|
+
# that mirrors what the loader actually read, so a save can rewrite
|
|
7
|
+
# the file instead of regenerating it from the heap.
|
|
8
|
+
#
|
|
9
|
+
# The top level plays the same role as an Source::ObjNode's children
|
|
10
|
+
# list -- there is no special "root" wrapper, just the file's own
|
|
11
|
+
# ordered content, which may include more than one top-level object
|
|
12
|
+
# declaration (a file may legitimately declare several roots).
|
|
13
|
+
#
|
|
14
|
+
|
|
15
|
+
module Gloo
|
|
16
|
+
module Persist
|
|
17
|
+
module Source
|
|
18
|
+
class SourceDoc
|
|
19
|
+
|
|
20
|
+
attr_reader :children
|
|
21
|
+
|
|
22
|
+
#
|
|
23
|
+
# Set up an empty source document.
|
|
24
|
+
#
|
|
25
|
+
def initialize
|
|
26
|
+
@children = []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# The top-level object declarations in this file (usually one,
|
|
31
|
+
# per the "a file owns its declarations" model -- more than one
|
|
32
|
+
# only when the file legitimately declares several roots).
|
|
33
|
+
#
|
|
34
|
+
def roots
|
|
35
|
+
return @children.select { |n| n.is_a?( Source::ObjNode ) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/gloo/verbs/load.rb
CHANGED
|
@@ -124,6 +124,11 @@ module Gloo
|
|
|
124
124
|
"#{UNKNOWN_OPT_ERR} — The reference type given isn't file, ext, or lib.",
|
|
125
125
|
"#{WRONG_NUM_ARGS_ERR} — load expects 2 or 3 arguments (the verb, an optional reference type, and the file/ext/lib name)."
|
|
126
126
|
],
|
|
127
|
+
:notes => 'Several files can contribute to one container: ' \
|
|
128
|
+
'declare it in each file and add different children, and ' \
|
|
129
|
+
'they merge in the heap. If two loaded files declare the ' \
|
|
130
|
+
'same object with different values, the first one loaded ' \
|
|
131
|
+
'wins and a warning is logged.',
|
|
127
132
|
:examples => <<~EXAMPLES.strip
|
|
128
133
|
> load my/project/file
|
|
129
134
|
> load my/app/*
|
data/lib/gloo/verbs/reload.rb
CHANGED
|
@@ -61,6 +61,9 @@ module Gloo
|
|
|
61
61
|
'loaded again. Note that re-load does not trigger the ' \
|
|
62
62
|
'on_load script to run. There is an on_reload message sent ' \
|
|
63
63
|
'to all open files.',
|
|
64
|
+
:notes => 'If a file has changes that have not been saved, ' \
|
|
65
|
+
'reloading discards them (a warning is logged, but the ' \
|
|
66
|
+
'reload still proceeds).',
|
|
64
67
|
:examples => <<~EXAMPLES.strip
|
|
65
68
|
> reload
|
|
66
69
|
EXAMPLES
|
data/lib/gloo/verbs/save.rb
CHANGED
|
@@ -10,14 +10,16 @@ module Gloo
|
|
|
10
10
|
|
|
11
11
|
KEYWORD = 'save'.freeze
|
|
12
12
|
KEYWORD_SHORT = 'sv'.freeze
|
|
13
|
+
TO = 'to'.freeze
|
|
14
|
+
MISSING_PATH_ERR = "'Save ... to' must include a path!".freeze
|
|
13
15
|
|
|
14
16
|
#
|
|
15
17
|
# Run the verb.
|
|
16
18
|
#
|
|
17
19
|
def run
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
return @engine.persist_man.save @tokens.second unless @tokens.index_of( TO )
|
|
21
|
+
|
|
22
|
+
run_save_to
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
#
|
|
@@ -34,6 +36,26 @@ module Gloo
|
|
|
34
36
|
return KEYWORD_SHORT
|
|
35
37
|
end
|
|
36
38
|
|
|
39
|
+
# ---------------------------------------------------------------------
|
|
40
|
+
# Private functions
|
|
41
|
+
# ---------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
#
|
|
46
|
+
# Run 'save {obj} to {path}'.
|
|
47
|
+
#
|
|
48
|
+
def run_save_to
|
|
49
|
+
name = @tokens.before_token( TO )[ 1 ]
|
|
50
|
+
path = @tokens.after_token( TO )
|
|
51
|
+
unless path
|
|
52
|
+
@engine.err MISSING_PATH_ERR
|
|
53
|
+
return
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
@engine.persist_man.save_to( name, path )
|
|
57
|
+
end
|
|
58
|
+
|
|
37
59
|
# ---------------------------------------------------------------------
|
|
38
60
|
# Verb Documentation
|
|
39
61
|
# ---------------------------------------------------------------------
|
|
@@ -45,17 +67,42 @@ module Gloo
|
|
|
45
67
|
{
|
|
46
68
|
:name => KEYWORD,
|
|
47
69
|
:shortcut => KEYWORD_SHORT,
|
|
48
|
-
:description => 'Save
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
|
|
70
|
+
:description => 'Save an object to a .gloo file. With no ' \
|
|
71
|
+
'path, saves every file that owns a declaration in the ' \
|
|
72
|
+
"object's root (its file, or every contributing file for " \
|
|
73
|
+
'a namespace shared across several files); with no object ' \
|
|
74
|
+
'at all, saves every open file. An object not yet mapped ' \
|
|
75
|
+
'to a file is saved fresh, either to a given path or to a ' \
|
|
76
|
+
"default path built from the object's own name.",
|
|
77
|
+
:syntax => [
|
|
78
|
+
'save',
|
|
79
|
+
'save {path.to.object}',
|
|
80
|
+
'save {path.to.object} to {path}'
|
|
81
|
+
],
|
|
52
82
|
:parameters => [
|
|
53
|
-
'{path.to.object} — Name of the object
|
|
83
|
+
'{path.to.object} — Name of the object to save.',
|
|
84
|
+
'{path} — Where to save it, relative to the project root ' \
|
|
85
|
+
'(.gloo appended if omitted). Registers the mapping, so ' \
|
|
86
|
+
'a later bare save includes it.'
|
|
87
|
+
],
|
|
88
|
+
:result => 'The file(s) are updated with the latest object ' \
|
|
89
|
+
'state. This is a rewrite, not a regeneration: comments, ' \
|
|
90
|
+
'blank lines, and the original formatting are preserved, ' \
|
|
91
|
+
'and only values that actually changed are re-written.',
|
|
92
|
+
:errors => [
|
|
93
|
+
"#{MISSING_PATH_ERR} — 'to' was given with nothing after it.",
|
|
94
|
+
'Could not resolve object to save — the object was not found.',
|
|
95
|
+
'Will not overwrite a file not already saved there — the ' \
|
|
96
|
+
'target path exists but is not mapped to this object.'
|
|
54
97
|
],
|
|
55
|
-
:
|
|
56
|
-
|
|
98
|
+
:notes => 'An object can also be told to save itself: ' \
|
|
99
|
+
'`tell my_obj to save`. When several files contribute to ' \
|
|
100
|
+
'one container (the namespace pattern), each file save only ' \
|
|
101
|
+
"rewrites that file's own declarations.",
|
|
57
102
|
:examples => <<~EXAMPLES.strip
|
|
103
|
+
> save
|
|
58
104
|
> save my_obj
|
|
105
|
+
> save my_obj to sub/my_obj
|
|
59
106
|
EXAMPLES
|
|
60
107
|
}
|
|
61
108
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Nested container shorthand tests
|
|
3
|
+
#
|
|
4
|
+
# A dotted name in a declaration (page.core.title [string] :) creates
|
|
5
|
+
# a container for each prefix segment, then declares the real object
|
|
6
|
+
# under the last one.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
tests [can] :
|
|
10
|
+
lang [can] :
|
|
11
|
+
shorthand [can] :
|
|
12
|
+
|
|
13
|
+
page.core.title [string] : Home
|
|
14
|
+
page.core.count [int] : 3
|
|
15
|
+
|
|
16
|
+
builds_the_container_chain [test] :
|
|
17
|
+
description [string] : a dotted name creates a container for each prefix segment.
|
|
18
|
+
on_test [script] :
|
|
19
|
+
eval ^^.page.core.title = 'Home'
|
|
20
|
+
assert "expected the shorthand-built value to be reachable"
|
|
21
|
+
|
|
22
|
+
check ^^.page for contains?
|
|
23
|
+
assert "expected 'page' to be a real container"
|
|
24
|
+
|
|
25
|
+
reuses_an_existing_prefix [test] :
|
|
26
|
+
description [string] : a second shorthand line reuses the same containers.
|
|
27
|
+
on_test [script] :
|
|
28
|
+
eval ^^.page.core.count = 3
|
|
29
|
+
assert "expected both shorthand lines to share the 'core' container"
|
|
@@ -20,6 +20,7 @@ tests [can] :
|
|
|
20
20
|
built [json] :
|
|
21
21
|
out [can] :
|
|
22
22
|
found [string] :
|
|
23
|
+
assigned [json] :
|
|
23
24
|
|
|
24
25
|
get_a_value [test] :
|
|
25
26
|
description [string] : get pulls a value out by dotted path.
|
|
@@ -68,3 +69,11 @@ tests [can] :
|
|
|
68
69
|
tell ^^.compact to get ( 'b.c' )
|
|
69
70
|
eval it = '2'
|
|
70
71
|
assert "expected the value to still be reachable after pretty"
|
|
72
|
+
|
|
73
|
+
assign_a_json_string_at_run_time [test] :
|
|
74
|
+
description [string] : a quote-containing literal can be put into an object at run time.
|
|
75
|
+
on_test [script] :
|
|
76
|
+
put '{"lang":"gloo","ok":true}' into tests.objs.json.assigned
|
|
77
|
+
tell ^^.assigned to get ( 'lang' )
|
|
78
|
+
eval it = 'gloo'
|
|
79
|
+
assert "expected the run-time JSON string to be assigned and readable"
|
|
@@ -12,6 +12,14 @@ tests [can] :
|
|
|
12
12
|
three 3 tree
|
|
13
13
|
END
|
|
14
14
|
|
|
15
|
+
# inside a block every line is literal - '#' lines and blank
|
|
16
|
+
# lines are kept, not treated as comments/blanks
|
|
17
|
+
hashed [text] : BEGIN
|
|
18
|
+
# a heading
|
|
19
|
+
|
|
20
|
+
- a list item
|
|
21
|
+
END
|
|
22
|
+
|
|
15
23
|
b [string] :
|
|
16
24
|
|
|
17
25
|
verify_text [test] :
|
|
@@ -56,12 +64,25 @@ tests [can] :
|
|
|
56
64
|
assert "expected text to have 5 words"
|
|
57
65
|
|
|
58
66
|
count_lines [test] :
|
|
59
|
-
description [string] : Get the number of lines
|
|
67
|
+
description [string] : Get the number of lines
|
|
60
68
|
on_test [script] :
|
|
61
69
|
tell ^^.a to count_lines
|
|
62
70
|
eval it = 3
|
|
63
71
|
assert "expected text to have 3 lines"
|
|
64
72
|
|
|
73
|
+
block_keeps_hash_and_blank_lines [test] :
|
|
74
|
+
description [string] : a BEGIN/END block keeps '#' lines and blank lines verbatim
|
|
75
|
+
on_test [script] :
|
|
76
|
+
check ^^.hashed for substring? ( "# a heading" )
|
|
77
|
+
assert "expected the '#' line to be kept, not stripped as a comment"
|
|
78
|
+
|
|
79
|
+
check ^^.hashed for substring? ( "- a list item" )
|
|
80
|
+
assert "expected content after the blank line to be kept"
|
|
81
|
+
|
|
82
|
+
tell ^^.hashed to count_lines
|
|
83
|
+
eval it = 3
|
|
84
|
+
assert "expected 3 lines: heading, blank, list item"
|
|
85
|
+
|
|
65
86
|
starts_with [test] :
|
|
66
87
|
description [string] : Check if the string starts with a value
|
|
67
88
|
on_test [script] :
|
|
@@ -21,6 +21,21 @@ tests [can] :
|
|
|
21
21
|
eval ^^.s = "hello world"
|
|
22
22
|
assert "expected s to be 'hello world'"
|
|
23
23
|
|
|
24
|
+
put_a_quote_containing_value [test] :
|
|
25
|
+
description [string] : Put a literal that contains quote characters
|
|
26
|
+
on_test [script] :
|
|
27
|
+
put '{"x":1}' into ^^.s
|
|
28
|
+
eval ^^.s = '{"x":1}'
|
|
29
|
+
assert "expected s to hold the JSON string"
|
|
30
|
+
|
|
31
|
+
put "it's here" into ^^.s
|
|
32
|
+
eval ^^.s = "it's here"
|
|
33
|
+
assert "expected s to hold the apostrophe string"
|
|
34
|
+
|
|
35
|
+
put "say \"hi\"" into ^^.s
|
|
36
|
+
eval ^^.s = 'say "hi"'
|
|
37
|
+
assert "expected s to hold the unescaped double-quote string"
|
|
38
|
+
|
|
24
39
|
blank [test] :
|
|
25
40
|
description [string] : Check to see if the string is blank
|
|
26
41
|
on_test [script] :
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Reload message tests -- 'tell {obj} to reload' re-reads the file.
|
|
3
|
+
#
|
|
4
|
+
# The bare 'reload' verb restarts the engine (covered by the unit
|
|
5
|
+
# tests). These exercise per-object reload against a /tmp scratch file
|
|
6
|
+
# that gets rewritten between load and reload.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
tests [can] :
|
|
10
|
+
verbs [can] :
|
|
11
|
+
reload [can] :
|
|
12
|
+
|
|
13
|
+
f [file] : /tmp/gloo_reload_test.gloo
|
|
14
|
+
v1 [text] : BEGIN
|
|
15
|
+
demo [container] :
|
|
16
|
+
msg [string] : original
|
|
17
|
+
END
|
|
18
|
+
|
|
19
|
+
v2 [text] : BEGIN
|
|
20
|
+
demo [container] :
|
|
21
|
+
msg [string] : edited outside gloo
|
|
22
|
+
added [string] : brand new
|
|
23
|
+
END
|
|
24
|
+
|
|
25
|
+
reload_picks_up_an_external_change [test] :
|
|
26
|
+
description [string] : editing the file on disk then reloading takes effect.
|
|
27
|
+
on_test [script] :
|
|
28
|
+
tell ^^.f to write ( ^^.v1 )
|
|
29
|
+
load /tmp/gloo_reload_test.gloo
|
|
30
|
+
eval demo.msg = 'original'
|
|
31
|
+
assert "sanity: loaded the first version"
|
|
32
|
+
|
|
33
|
+
tell ^^.f to write ( ^^.v2 )
|
|
34
|
+
tell demo to reload
|
|
35
|
+
|
|
36
|
+
eval demo.msg = 'edited outside gloo'
|
|
37
|
+
assert "reload should pick up the changed value"
|
|
38
|
+
eval demo.added = 'brand new'
|
|
39
|
+
assert "reload should pick up a newly added child"
|
|
40
|
+
|
|
41
|
+
tell demo to unload
|
|
42
|
+
tell ^^.f to delete
|
|
43
|
+
|
|
44
|
+
reload_discards_an_in_memory_edit [test] :
|
|
45
|
+
description [string] : an unsaved change is thrown away by reload.
|
|
46
|
+
on_test [script] :
|
|
47
|
+
tell ^^.f to write ( ^^.v1 )
|
|
48
|
+
load /tmp/gloo_reload_test.gloo
|
|
49
|
+
|
|
50
|
+
put 'not saved' into demo.msg
|
|
51
|
+
tell demo to reload
|
|
52
|
+
|
|
53
|
+
eval demo.msg = 'original'
|
|
54
|
+
assert "reload should restore the on-disk value"
|
|
55
|
+
|
|
56
|
+
tell demo to unload
|
|
57
|
+
tell ^^.f to delete
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Save verb tests -- real load -> edit -> save round trips.
|
|
3
|
+
#
|
|
4
|
+
# Each test writes a .gloo fixture to /tmp, loads it, does something,
|
|
5
|
+
# saves, reads the file back, and asserts on the raw text. Every test
|
|
6
|
+
# unloads its object and deletes its scratch file at the end so the
|
|
7
|
+
# next one starts clean.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
tests [can] :
|
|
11
|
+
verbs [can] :
|
|
12
|
+
save [can] :
|
|
13
|
+
f [file] : /tmp/gloo_save_test.gloo
|
|
14
|
+
g [file] : /tmp/gloo_save_test_2.gloo
|
|
15
|
+
to_target [file] : /tmp/gloo_save_test_to.gloo
|
|
16
|
+
out [string] : BEGIN
|
|
17
|
+
#
|
|
18
|
+
# a comment above demo
|
|
19
|
+
#
|
|
20
|
+
demo [container] :
|
|
21
|
+
msg [string] : hello
|
|
22
|
+
END
|
|
23
|
+
err [bool] : false
|
|
24
|
+
|
|
25
|
+
on_error [script] :
|
|
26
|
+
put true into ^.err
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# The fixture used by most tests: two comments, a changed-in-place
|
|
30
|
+
# value, and an untouched sibling.
|
|
31
|
+
#
|
|
32
|
+
fixture [text] : BEGIN
|
|
33
|
+
#
|
|
34
|
+
# a comment above demo
|
|
35
|
+
#
|
|
36
|
+
demo [container] :
|
|
37
|
+
msg [string] : hello
|
|
38
|
+
# a comment above other
|
|
39
|
+
other [string] : world
|
|
40
|
+
END
|
|
41
|
+
|
|
42
|
+
round_trip_preserves_comments_and_untouched_lines [test] :
|
|
43
|
+
description [string] : load, change one value, save -- comments and the untouched sibling survive.
|
|
44
|
+
on_test [script] :
|
|
45
|
+
put false into ^^.err
|
|
46
|
+
tell ^^.f to write ( ^^.fixture )
|
|
47
|
+
load /tmp/gloo_save_test.gloo
|
|
48
|
+
put 'changed' into demo.msg
|
|
49
|
+
save demo
|
|
50
|
+
eval ^^.err = false
|
|
51
|
+
assert "save should not error"
|
|
52
|
+
tell ^^.f to read ( ^^.out )
|
|
53
|
+
check ^^.out for substring? ( "# a comment above demo" )
|
|
54
|
+
assert "the leading comment block should survive"
|
|
55
|
+
check ^^.out for substring? ( "# a comment above other" )
|
|
56
|
+
assert "the inner comment should survive"
|
|
57
|
+
check ^^.out for substring? ( "msg [string] : changed" )
|
|
58
|
+
assert "the changed value should be written"
|
|
59
|
+
check ^^.out for substring? ( "other [string] : world" )
|
|
60
|
+
assert "the untouched sibling should be unchanged"
|
|
61
|
+
tell demo to unload
|
|
62
|
+
tell ^^.f to delete
|
|
63
|
+
unchanged_file_round_trips_byte_for_byte [test] :
|
|
64
|
+
description [string] : load then save with no change reproduces the file exactly.
|
|
65
|
+
on_test [script] :
|
|
66
|
+
tell ^^.f to write ( ^^.fixture )
|
|
67
|
+
load /tmp/gloo_save_test.gloo
|
|
68
|
+
save demo
|
|
69
|
+
tell ^^.f to read ( ^^.out )
|
|
70
|
+
eval ^^.out = ^^.fixture
|
|
71
|
+
assert "an unchanged save should reproduce the original text"
|
|
72
|
+
tell demo to unload
|
|
73
|
+
tell ^^.f to delete
|
|
74
|
+
deleting_an_object_drops_its_line [test] :
|
|
75
|
+
description [string] : removing a child and saving drops just that declaration.
|
|
76
|
+
on_test [script] :
|
|
77
|
+
tell ^^.f to write ( ^^.fixture )
|
|
78
|
+
load /tmp/gloo_save_test.gloo
|
|
79
|
+
tell demo.other to unload
|
|
80
|
+
save demo
|
|
81
|
+
tell ^^.f to read ( ^^.out )
|
|
82
|
+
check ^^.out for substring? ( "msg [string] : hello" )
|
|
83
|
+
assert "the kept sibling should still be there"
|
|
84
|
+
check ^^.out for substring? ( "other [string]" )
|
|
85
|
+
refute "the removed child's line should be gone"
|
|
86
|
+
tell demo to unload
|
|
87
|
+
tell ^^.f to delete
|
|
88
|
+
save_to_a_new_path_creates_the_file [test] :
|
|
89
|
+
description [string] : save {obj} to {path} writes a new file and registers it.
|
|
90
|
+
on_test [script] :
|
|
91
|
+
tell ^^.f to write ( ^^.fixture )
|
|
92
|
+
load /tmp/gloo_save_test.gloo
|
|
93
|
+
save demo to /tmp/gloo_save_test_to
|
|
94
|
+
check ^^.to_target for exists?
|
|
95
|
+
assert "save ... to should create the target file"
|
|
96
|
+
tell ^^.to_target to read ( ^^.out )
|
|
97
|
+
check ^^.out for substring? ( "msg [string] : hello" )
|
|
98
|
+
assert "the new file should hold the object"
|
|
99
|
+
tell demo to unload
|
|
100
|
+
tell ^^.f to delete
|
|
101
|
+
tell ^^.to_target to delete
|
|
102
|
+
reload_discards_unsaved_changes [test] :
|
|
103
|
+
description [string] : reload throws away edits that were not saved.
|
|
104
|
+
on_test [script] :
|
|
105
|
+
tell ^^.f to write ( ^^.fixture )
|
|
106
|
+
load /tmp/gloo_save_test.gloo
|
|
107
|
+
put 'not saved' into demo.msg
|
|
108
|
+
eval demo.msg = 'not saved'
|
|
109
|
+
assert "sanity: the edit landed"
|
|
110
|
+
tell demo to reload
|
|
111
|
+
eval demo.msg = 'hello'
|
|
112
|
+
assert "reload should restore the on-disk value"
|
|
113
|
+
tell demo to unload
|
|
114
|
+
tell ^^.f to delete
|
|
115
|
+
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gloo
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Crane
|
|
@@ -384,12 +384,22 @@ files:
|
|
|
384
384
|
- lib/gloo/objs/web/http_post.rb
|
|
385
385
|
- lib/gloo/objs/web/json.rb
|
|
386
386
|
- lib/gloo/objs/web/uri.rb
|
|
387
|
+
- lib/gloo/persist/comment_buffer.rb
|
|
388
|
+
- lib/gloo/persist/declaration_ledger.rb
|
|
387
389
|
- lib/gloo/persist/disc_mech.rb
|
|
388
390
|
- lib/gloo/persist/file_loader.rb
|
|
389
391
|
- lib/gloo/persist/file_saver.rb
|
|
390
392
|
- lib/gloo/persist/file_storage.rb
|
|
393
|
+
- lib/gloo/persist/indent_stack.rb
|
|
391
394
|
- lib/gloo/persist/line_splitter.rb
|
|
392
395
|
- lib/gloo/persist/persist_man.rb
|
|
396
|
+
- lib/gloo/persist/script_body_collector.rb
|
|
397
|
+
- lib/gloo/persist/shorthand_expander.rb
|
|
398
|
+
- lib/gloo/persist/source/blank_node.rb
|
|
399
|
+
- lib/gloo/persist/source/comment_node.rb
|
|
400
|
+
- lib/gloo/persist/source/directive_node.rb
|
|
401
|
+
- lib/gloo/persist/source/obj_node.rb
|
|
402
|
+
- lib/gloo/persist/source/source_doc.rb
|
|
393
403
|
- lib/gloo/plugin/base.rb
|
|
394
404
|
- lib/gloo/plugin/callback.rb
|
|
395
405
|
- lib/gloo/plugin/ext_manager.rb
|
|
@@ -443,6 +453,7 @@ files:
|
|
|
443
453
|
- test.gloo/lang/load_lib_directive.test.gloo
|
|
444
454
|
- test.gloo/lang/naming.test.gloo
|
|
445
455
|
- test.gloo/lang/ops.test.gloo
|
|
456
|
+
- test.gloo/lang/shorthand.test.gloo
|
|
446
457
|
- test.gloo/math/add.test.gloo
|
|
447
458
|
- test.gloo/math/div.test.gloo
|
|
448
459
|
- test.gloo/math/mult.test.gloo
|
|
@@ -481,7 +492,9 @@ files:
|
|
|
481
492
|
- test.gloo/verbs/log.test.gloo
|
|
482
493
|
- test.gloo/verbs/move.test.gloo
|
|
483
494
|
- test.gloo/verbs/put.test.gloo
|
|
495
|
+
- test.gloo/verbs/reload.test.gloo
|
|
484
496
|
- test.gloo/verbs/run.test.gloo
|
|
497
|
+
- test.gloo/verbs/save.test.gloo
|
|
485
498
|
- test.gloo/verbs/show.test.gloo
|
|
486
499
|
- test.gloo/verbs/tell.test.gloo
|
|
487
500
|
- test.gloo/verbs/throw.test.gloo
|