gloo 7.1.2 → 7.2

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +23 -0
  3. data/docs/application.md +47 -2
  4. data/docs/development.md +10 -0
  5. data/docs/iterators.md +22 -5
  6. data/docs/language_objects.md +20 -15
  7. data/docs/language_scripting.md +6 -2
  8. data/docs/plugins.md +1 -1
  9. data/docs/testing.md +4 -7
  10. data/docs/verbs.md +23 -3
  11. data/lib/VERSION +1 -1
  12. data/lib/VERSION_NOTES +13 -0
  13. data/lib/dependencies.rb +4 -1
  14. data/lib/gloo/app/engine.rb +18 -6
  15. data/lib/gloo/core/obj.rb +0 -23
  16. data/lib/gloo/exec/script.rb +12 -2
  17. data/lib/gloo/objs/basic/script.rb +5 -28
  18. data/lib/gloo/objs/ctrl/each.rb +4 -3
  19. data/lib/gloo/objs/ctrl/repeat.rb +4 -3
  20. data/lib/gloo/persist/declaration_ledger.rb +9 -9
  21. data/lib/gloo/persist/declaration_writer.rb +208 -0
  22. data/lib/gloo/persist/disc_mech.rb +70 -39
  23. data/lib/gloo/persist/file_loader.rb +125 -66
  24. data/lib/gloo/persist/file_saver.rb +130 -198
  25. data/lib/gloo/persist/file_storage.rb +15 -4
  26. data/lib/gloo/persist/file_style.rb +89 -0
  27. data/lib/gloo/persist/line_splitter.rb +16 -1
  28. data/lib/gloo/persist/persist_man.rb +112 -30
  29. data/lib/gloo/persist/save_index.rb +211 -0
  30. data/lib/gloo/persist/script_body_collector.rb +16 -21
  31. data/lib/gloo/persist/source/blank_node.rb +10 -3
  32. data/lib/gloo/persist/source/comment_node.rb +10 -2
  33. data/lib/gloo/persist/source/directive_node.rb +10 -2
  34. data/lib/gloo/persist/source/obj_node.rb +174 -9
  35. data/lib/gloo/persist/source/source_doc.rb +54 -0
  36. data/lib/gloo/persist/subtree_extractor.rb +9 -37
  37. data/lib/gloo/persist/trivia_buffer.rb +7 -7
  38. data/lib/gloo/verbs/break.rb +99 -10
  39. data/lib/gloo/verbs/load.rb +27 -21
  40. data/lib/gloo/verbs/move.rb +42 -3
  41. data/lib/gloo/verbs/save.rb +26 -4
  42. data/test.gloo/app/exit_status.test.gloo +34 -0
  43. data/test.gloo/app/fixtures/exit_after_error.gloo +9 -0
  44. data/test.gloo/lang/declaration.test.gloo +36 -0
  45. data/test.gloo/lang/fixtures/mixed_indent.gloo +5 -0
  46. data/test.gloo/lang/naming.test.gloo +44 -6
  47. data/test.gloo/verbs/break.test.gloo +104 -11
  48. data/test.gloo/verbs/fixtures/load_beside.gloo +6 -0
  49. data/test.gloo/verbs/fixtures/lossless_text.gloo +7 -0
  50. data/test.gloo/verbs/fixtures/lossless_text_saved.gloo +9 -0
  51. data/test.gloo/verbs/load.test.gloo +18 -0
  52. data/test.gloo/verbs/move.test.gloo +39 -0
  53. data/test.gloo/verbs/save.test.gloo +239 -0
  54. metadata +10 -1
@@ -3,10 +3,10 @@
3
3
  #
4
4
  # One object declaration in a source file, eg.
5
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.
6
+ # Carries the declaration's exact source text, plus a back-reference
7
+ # to the heap object it created (or named again), so a save can tell
8
+ # whether the object is still there and its value unchanged and, if
9
+ # so, write the text back exactly as it was read.
10
10
  #
11
11
 
12
12
  module Gloo
@@ -19,8 +19,23 @@ module Gloo
19
19
  # :begin_end -- name [type] : BEGIN ... END
20
20
  # :body -- name [type] :, followed by an indented,
21
21
  # unparsed body (script commands)
22
+ #
23
+ # raw is the declaration's own exact text: its line (with any
24
+ # continuation lines), and its BEGIN/END block or script body,
25
+ # line endings included -- not its leading_doc or children.
26
+ # head is the declaration line up to its value (indent, name and
27
+ # [type], as written), raw_tail what follows it.
28
+ #
29
+ # repaired_head is set when gloo had to guess at the head (a
30
+ # missing name or ']'): a save writes the head as understood.
31
+ #
32
+ # home is the object's parent and snapshot a copy of its value,
33
+ # both taken when the file finished loading: the object is still
34
+ # in its place if its parent is home, and unchanged if its value
35
+ # equals the snapshot.
22
36
  attr_accessor :name, :raw_type, :raw_indent, :raw_tail, :block_style,
23
- :raw_value, :raw_end_indent, :leading_doc, :trailing_comment, :obj
37
+ :raw_value, :leading_doc, :obj, :raw, :head,
38
+ :home, :snapshot, :repaired_head
24
39
  attr_reader :children
25
40
 
26
41
  #
@@ -35,15 +50,80 @@ module Gloo
35
50
  @raw_tail = ''
36
51
  @block_style = :inline
37
52
  @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
53
  @leading_doc = nil
42
- @trailing_comment = nil
43
54
  @obj = nil
55
+ @raw = ''
56
+ @head = ''
57
+ @home = nil
58
+ @snapshot = nil
59
+ @repaired_head = nil
44
60
  @children = []
45
61
  end
46
62
 
63
+ #
64
+ # The exact source text of the declaration, its leading_doc and
65
+ # everything under it, as read.
66
+ #
67
+ def text
68
+ str = "#{@leading_doc}#{@raw}"
69
+ @children.each { |child| str << child.text }
70
+ return str
71
+ end
72
+
73
+ #
74
+ # A copy of this declaration with no children, and with the
75
+ # given text in place of its own (for a save's new document;
76
+ # this node is left as it is).
77
+ #
78
+ def detached( raw = @raw )
79
+ copy = dup
80
+ copy.raw = raw
81
+ copy.reset_children
82
+ return copy
83
+ end
84
+
85
+ #
86
+ # A copy with the head gloo understood in place of the one
87
+ # written (see repaired_head); this node if nothing was repaired.
88
+ #
89
+ def repaired
90
+ return self unless @repaired_head
91
+
92
+ copy = detached( @raw.sub( @head, @repaired_head ) )
93
+ copy.head = @repaired_head
94
+ copy.repaired_head = nil
95
+ return copy
96
+ end
97
+
98
+ #
99
+ # Record where the object is and what its value is, now that the
100
+ # file has finished loading.
101
+ #
102
+ def remember
103
+ return unless @obj
104
+
105
+ @home = @obj.parent
106
+ @snapshot = copy_of( @obj.value )
107
+ end
108
+
109
+ #
110
+ # Is the object still under the parent it was loaded under?
111
+ #
112
+ def in_place?
113
+ return false unless @obj && @home
114
+
115
+ return @obj.parent.equal?( @home ) && @home.children.any? { |c| c.equal?( @obj ) }
116
+ end
117
+
118
+ #
119
+ # Has the object's value changed since the file was loaded?
120
+ #
121
+ def changed?
122
+ return false unless @obj
123
+
124
+ return @obj.value != @snapshot
125
+ end
126
+
47
127
  #
48
128
  # The cleaned-up leading_doc: each line's leading whitespace and
49
129
  # its '#' marker (plus one following space, if any) stripped,
@@ -59,8 +139,93 @@ module Gloo
59
139
  return dedent( lines ).join( "\n" )
60
140
  end
61
141
 
142
+ #
143
+ # Give the declaration a new name as written (eg. a dotted path
144
+ # when it moves into a file of its own). Only the name on its
145
+ # first line changes.
146
+ #
147
+ def rename( new_name )
148
+ @raw = @raw.sub( /\A([ \t]*)#{Regexp.escape( @name )}/ ) { "#{$1}#{new_name}" }
149
+ @head = @head.sub( /\A([ \t]*)#{Regexp.escape( @name )}/ ) { "#{$1}#{new_name}" }
150
+ @name = new_name
151
+ end
152
+
153
+ #
154
+ # Move the declaration (and everything under it) to a new
155
+ # indentation. Its own lines and its script body shift by the
156
+ # same amount; a BEGIN/END block's content is the value, so it
157
+ # stays exactly as it is, and END lines up with the declaration.
158
+ # A leading_doc's lines are set to the new indentation.
159
+ #
160
+ def reindent( new_indent )
161
+ old = @raw_indent
162
+ @leading_doc = reindent_doc( @leading_doc, new_indent ) if @leading_doc
163
+ @raw = reindent_own( @raw, old, new_indent )
164
+ @head = shift( @head, old, new_indent )
165
+ @raw_indent = new_indent
166
+ @children.each do |child|
167
+ next unless child.is_a?( ObjNode )
168
+
169
+ child.reindent( shift( child.raw_indent, old, new_indent ) )
170
+ end
171
+ end
172
+
173
+ protected
174
+
175
+ #
176
+ # Start with no children (for a detached copy).
177
+ #
178
+ def reset_children
179
+ @children = []
180
+ end
181
+
62
182
  private
63
183
 
184
+ #
185
+ # A copy of a value that can't be changed through the original
186
+ # (a script's lines are an Array that grows in place).
187
+ #
188
+ def copy_of( value )
189
+ return Marshal.load( Marshal.dump( value ) )
190
+ rescue TypeError
191
+ return value.dup
192
+ end
193
+
194
+ #
195
+ # The declaration's own lines at a new indentation.
196
+ #
197
+ def reindent_own( text, old, new_indent )
198
+ lines = text.lines
199
+ return text if lines.empty?
200
+
201
+ if @block_style == :begin_end
202
+ ending = lines.last[ /\r?\n\z/ ].to_s
203
+ lines[ 0 ] = shift( lines[ 0 ], old, new_indent )
204
+ lines[ -1 ] = "#{new_indent}END#{ending}" if lines.size > 1
205
+ return lines.join
206
+ end
207
+
208
+ return lines.map { |l| l.strip.empty? ? l : shift( l, old, new_indent ) }.join
209
+ end
210
+
211
+ #
212
+ # Replace a line's old indentation prefix with the new one; a
213
+ # line that doesn't start with it gets the new one added.
214
+ #
215
+ def shift( line, old, new_indent )
216
+ return "#{new_indent}#{line.delete_prefix( old )}" if line.start_with?( old )
217
+
218
+ return "#{new_indent}#{line.lstrip}"
219
+ end
220
+
221
+ #
222
+ # Re-indent a leading_doc's lines to new_indent, replacing
223
+ # whatever leading whitespace each '#' line had.
224
+ #
225
+ def reindent_doc( raw, new_indent )
226
+ return raw.lines.map { |line| "#{new_indent}#{line.lstrip}" }.join
227
+ end
228
+
64
229
  #
65
230
  # One raw comment line -> its text past the '#': leading
66
231
  # whitespace dropped, then the '#' and at most one space after
@@ -19,11 +19,53 @@ module Gloo
19
19
 
20
20
  attr_reader :children
21
21
 
22
+ # A file whose last line has no line ending: every node's text
23
+ # ends with one anyway (so new text can follow any node), and
24
+ # the document leaves the last one off.
25
+ attr_accessor :no_final_newline
26
+
22
27
  #
23
28
  # Set up an empty source document.
24
29
  #
25
30
  def initialize
26
31
  @children = []
32
+ @no_final_newline = false
33
+ end
34
+
35
+ #
36
+ # The file's exact text: every node's text, in order. Right
37
+ # after a load this is the file, byte for byte.
38
+ #
39
+ def text
40
+ str = @children.map( &:text ).join
41
+ str = str.delete_suffix( "\n" ) if @no_final_newline
42
+ return str
43
+ end
44
+
45
+ #
46
+ # Note that the file's last line had no line ending, and give it
47
+ # one in the node that holds it (see no_final_newline).
48
+ #
49
+ def missing_final_newline!
50
+ leaf = last_text_node( @children )
51
+ return unless leaf
52
+
53
+ leaf.raw << "\n"
54
+ @no_final_newline = true
55
+ end
56
+
57
+ #
58
+ # Every object declaration in the document, at any depth, in
59
+ # file order.
60
+ #
61
+ def obj_nodes( nodes = @children, found = [] )
62
+ nodes.each do |node|
63
+ next unless node.is_a?( Source::ObjNode )
64
+
65
+ found << node
66
+ obj_nodes( node.children, found )
67
+ end
68
+ return found
27
69
  end
28
70
 
29
71
  #
@@ -51,6 +93,18 @@ module Gloo
51
93
 
52
94
  private
53
95
 
96
+ #
97
+ # The node whose text comes last in the file: the last node, or
98
+ # its last child (at any depth) if it has children.
99
+ #
100
+ def last_text_node( nodes )
101
+ last = nodes.last
102
+ return nil unless last
103
+ return last unless last.is_a?( Source::ObjNode ) && last.children.any?
104
+
105
+ return last_text_node( last.children )
106
+ end
107
+
54
108
  #
55
109
  # Depth-first search of nodes (and their children) for the one
56
110
  # linked to obj; removes it from whichever children array it's
@@ -46,11 +46,13 @@ module Gloo
46
46
  return err( SPANS_MULTIPLE_FILES_ERR ) if owners.count > 1
47
47
 
48
48
  source_fs = owners.first
49
- node = source_fs ? remove_node( obj, source_fs ) : fresh_node( obj )
49
+ return [ fresh_node( obj ), nil ] unless source_fs
50
+
51
+ node = remove_node( obj, source_fs )
50
52
  return err( NO_OWN_DECLARATION_ERR ) unless node
51
53
 
52
- node.name = dotted_name( obj )
53
- reindent( node, 0 )
54
+ node.rename( dotted_name( obj ) )
55
+ node.reindent( '' )
54
56
  return [ node, source_fs ]
55
57
  end
56
58
 
@@ -110,13 +112,12 @@ module Gloo
110
112
 
111
113
  #
112
114
  # obj has no file of its own (never loaded, or created at
113
- # runtime since) -- build a plain node with no raw text of its
114
- # own. FileSaver renders it fresh from the live object, the same
115
- # way it already does for any brand-new child.
115
+ # runtime since) -- write it, and everything under it, from
116
+ # scratch, named by its dotted path.
116
117
  #
117
118
  def fresh_node( obj )
118
- node = Source::ObjNode.new( :name => obj.name, :raw_type => obj.type_display )
119
- node.obj = obj
119
+ node = FileSaver.new( @engine, nil, obj ).new_node( obj, dotted_name( obj ) )
120
+ Source::SourceDoc.new.obj_nodes( [ node ] ).each( &:remember )
120
121
  return node
121
122
  end
122
123
 
@@ -150,35 +151,6 @@ module Gloo
150
151
  return names.join( '.' )
151
152
  end
152
153
 
153
- #
154
- # Recompute raw_indent (and, for a BEGIN/END node, raw_end_indent
155
- # to match) from scratch for the moved node and every descendant
156
- # ObjNode. Their old indentation depth described nesting inside
157
- # the file they came from; here obj itself is depth 0, in a
158
- # brand-new file. Floating trivia (a blank line or comment not
159
- # attached as some declaration's leading_doc) travels along
160
- # unindented-adjusted -- purely cosmetic, since indentation never
161
- # affects how a comment/blank line is recognized on reload.
162
- #
163
- def reindent( node, depth )
164
- new_indent = "\t" * depth
165
- node.raw_indent = new_indent
166
- node.raw_end_indent = new_indent if node.block_style == :begin_end
167
- node.leading_doc = reindent_doc( node.leading_doc, new_indent ) if node.leading_doc
168
- node.children.each do |child|
169
- reindent( child, depth + 1 ) if child.is_a?( Source::ObjNode )
170
- end
171
- end
172
-
173
- #
174
- # Re-indent a raw leading_doc's lines to new_indent, replacing
175
- # whatever leading whitespace each '#'-prefixed line had in the
176
- # file it came from.
177
- #
178
- def reindent_doc( raw, new_indent )
179
- return raw.split( "\n" ).map { |line| "#{new_indent}#{line.lstrip}" }.join( "\n" )
180
- end
181
-
182
154
  end
183
155
  end
184
156
  end
@@ -29,16 +29,16 @@ module Gloo
29
29
  end
30
30
 
31
31
  #
32
- # Buffer one comment line. raw is the full source line (minus
33
- # its trailing newline); tabs is its indentation level.
32
+ # Buffer one comment line. raw is the full source line, with its
33
+ # line ending; tabs is its indentation level.
34
34
  #
35
35
  def push_comment( raw, tabs )
36
36
  @pending << { :kind => :comment, :raw => raw, :tabs => tabs }
37
37
  end
38
38
 
39
39
  #
40
- # Buffer one blank line. raw is its exact source text (may be
41
- # non-empty if it had trailing whitespace) -- a blank line always
40
+ # Buffer one blank line. raw is its exact source text, with its
41
+ # line ending and any trailing whitespace -- a blank line always
42
42
  # breaks a comment run's association with whatever declaration
43
43
  # follows, the same as it always has.
44
44
  #
@@ -61,8 +61,8 @@ module Gloo
61
61
  #
62
62
  # If the run since the last blank (or the whole run, if it has no
63
63
  # blank in it) is one or more comment lines all at the same
64
- # indent as line_tabs, claim it as a leading_doc (raw,
65
- # newline-joined). Anything before that -- an earlier blank, and
64
+ # indent as line_tabs, claim it as a leading_doc (the raw lines,
65
+ # line endings included). Anything before that -- an earlier blank, and
66
66
  # whatever preceded it -- floats into children first, in order,
67
67
  # since a blank always breaks the association. If the trailing
68
68
  # run doesn't qualify (wrong indent, or empty), it floats into
@@ -74,7 +74,7 @@ module Gloo
74
74
  flush_list( before, children )
75
75
 
76
76
  if after.any? && after.last[ :tabs ] == line_tabs
77
- doc = after.map { |t| t[ :raw ] }.join( "\n" )
77
+ doc = after.map { |t| t[ :raw ] }.join
78
78
  @pending = []
79
79
  return doc
80
80
  end
@@ -10,14 +10,23 @@ module Gloo
10
10
 
11
11
  KEYWORD = 'break'.freeze
12
12
  KEYWORD_SHORT = 'stop'.freeze
13
+ LOOP = 'loop'.freeze
14
+ NOT_IN_A_LOOP = 'Could not break loop: not in a loop.'.freeze
13
15
 
14
16
  #
15
17
  # Run the verb.
16
- # Stop the execution of the current script.
18
+ # Stop the execution of the current script. With loop (break
19
+ # loop), also end the loop whose do script this is.
17
20
  #
18
21
  def run
19
- warn_extra_words
20
- @engine.exec_env.running_script.break_out
22
+ words = extra_words.split( ' ' )
23
+ if words.first&.downcase == LOOP
24
+ warn_words_after_loop( words.drop( 1 ) )
25
+ break_loop
26
+ else
27
+ warn_unknown_words
28
+ @engine.exec_env.running_script.break_out
29
+ end
21
30
  end
22
31
 
23
32
  #
@@ -40,6 +49,54 @@ module Gloo
40
49
 
41
50
  private
42
51
 
52
+ #
53
+ # End the running script and its loop. Only valid directly in a
54
+ # loop's do script; anywhere else (a script the do runs, a group
55
+ # script, outside any loop) it's an error and nothing stops.
56
+ #
57
+ def break_loop
58
+ script = @engine.exec_env.running_script
59
+ unless loop_do?( script&.obj )
60
+ @engine.err NOT_IN_A_LOOP
61
+ return
62
+ end
63
+
64
+ script.break_out( loop: true )
65
+ end
66
+
67
+ #
68
+ # Is the object the do script of a loop (a repeat or an each)?
69
+ #
70
+ def loop_do?( obj )
71
+ parent = obj&.parent
72
+ return false unless parent.is_a?( Gloo::Objs::Repeat ) ||
73
+ parent.is_a?( Gloo::Objs::Each )
74
+
75
+ return parent.find_child( Gloo::Objs::Each::DO ).equal?( obj )
76
+ end
77
+
78
+ #
79
+ # Warn about anything written after break other than loop.
80
+ # The break still happens.
81
+ #
82
+ def warn_unknown_words
83
+ extra = extra_words
84
+ return if extra.empty?
85
+
86
+ @engine.warn "#{written_verb} takes only '#{LOOP}'; ignoring '#{extra}'."
87
+ end
88
+
89
+ #
90
+ # Warn about anything written after break loop. The loop still
91
+ # ends.
92
+ #
93
+ def warn_words_after_loop( words )
94
+ return if words.empty?
95
+
96
+ @engine.warn "#{written_verb} #{LOOP} takes no object; " \
97
+ "ignoring '#{words.join( ' ' )}'."
98
+ end
99
+
43
100
  # ---------------------------------------------------------------------
44
101
  # Verb Documentation
45
102
  # ---------------------------------------------------------------------
@@ -52,15 +109,26 @@ module Gloo
52
109
  :name => KEYWORD,
53
110
  :shortcut => KEYWORD_SHORT,
54
111
  :description => 'Break out of the current script, stop running. ' \
55
- 'If that script is the do script of a loop (repeat, or any ' \
56
- 'kind of each), the whole loop ends.',
57
- :syntax => [ 'break' ],
112
+ 'In a loop\'s do script, break skips the rest of that pass ' \
113
+ 'and the loop goes on; break loop ends the whole loop.',
114
+ :syntax => [ 'break', 'break loop' ],
115
+ :parameters => [
116
+ 'loop — Optional. Also end the loop whose do script this is. ' \
117
+ 'Only valid directly in a loop\'s do script (repeat, or any ' \
118
+ 'kind of each).'
119
+ ],
58
120
  :result => 'No more commands are executed in the currently ' \
59
121
  'running script. If there are other scripts in the stack, ' \
60
- 'they will resume normally. In a loop\'s do script, the loop ' \
61
- 'ends too and runs no more passes; the script that ran the ' \
62
- 'loop carries on. A break in a script the do script runs ' \
63
- '(eg. with run) ends only that script, not the loop.',
122
+ 'they will resume normally. In a loop\'s do script, a plain ' \
123
+ 'break ends that pass and the loop goes on with the next ' \
124
+ 'item. With break loop, the loop ends too and the script ' \
125
+ 'that ran it carries on.',
126
+ :errors => [
127
+ "#{NOT_IN_A_LOOP} — break loop in a script that isn't a " \
128
+ 'loop\'s do script: one the do script runs (with run, or a ' \
129
+ 'function), a loop\'s on_group_start or on_group_end, or a ' \
130
+ 'script outside any loop. Nothing stops.'
131
+ ],
64
132
  :examples => <<~EXAMPLES.strip
65
133
  #
66
134
  # Break out of a script.
@@ -73,6 +141,27 @@ module Gloo
73
141
  put true into ^.x
74
142
  if ^.x then break
75
143
  show 'should not see this'
144
+
145
+ #
146
+ # In a loop: break skips an item, break loop ends the loop.
147
+ # Shows 0, 1, 3, then 0, 1.
148
+ #
149
+ loops [can] :
150
+ on_load [script] :
151
+ tell loops.skip to run
152
+ tell loops.early to run
153
+ skip [repeat] :
154
+ times [int] : 4
155
+ index [int] :
156
+ do [script] :
157
+ if loops.skip.index = 2 then break
158
+ show loops.skip.index
159
+ early [repeat] :
160
+ times [int] : 4
161
+ index [int] :
162
+ do [script] :
163
+ if loops.early.index = 2 then break loop
164
+ show loops.early.index
76
165
  EXAMPLES
77
166
  }
78
167
  end
@@ -63,12 +63,15 @@ module Gloo
63
63
  return KEYWORD_SHORT
64
64
  end
65
65
 
66
- #
67
- # Load a gloo file
68
- #
66
+ #
67
+ # Load a gloo file. A relative name is looked for in the
68
+ # persist manager's search folders (next to this file, the
69
+ # app or projects folder, the gloo home).
70
+ #
69
71
  def load_gloo_file( fn )
70
72
  @engine.log.debug "Getting ready to load gloo file: #{fn}"
71
- @engine.persist_man.load fn
73
+ pm = @engine.persist_man
74
+ pm.load( fn, pm.search_folders )
72
75
  end
73
76
 
74
77
  #
@@ -98,16 +101,17 @@ module Gloo
98
101
  {
99
102
  :name => KEYWORD,
100
103
  :shortcut => KEYWORD_SHORT,
101
- :description => 'Load an object file. There are two ways to ' \
102
- 'specify the file. Give either the full path and file name ' \
103
- 'or use a relative path from the gloo project folder. For ' \
104
- 'the latter, the extension is not needed. For the former, ' \
105
- 'the file extension is necessary. Using * instead of a file ' \
106
- 'name will load all gloo files in the folder. When using the ' \
107
- '--app option, the file is loaded from the application ' \
108
- 'folder, but if not found there, the loader will also look ' \
109
- 'in the gloo root folder. The load verb is also used to ' \
110
- 'import an extension or library at runtime.',
104
+ :description => 'Load an object file. Give a full path, one ' \
105
+ 'starting with ~, or a relative name; the .gloo extension is ' \
106
+ 'optional. A relative name is looked for in a few places, in ' \
107
+ 'order. In App mode (--app): the app folder, then next to the ' \
108
+ 'file the loading script came from, then the gloo home. ' \
109
+ 'Otherwise: next to the loading file, then the projects ' \
110
+ 'folder (the project_path setting), then the gloo home. A ' \
111
+ 'command typed at the prompt has no file, so the working ' \
112
+ 'folder stands in for it. Using * instead of a file name ' \
113
+ 'loads all gloo files in the folder. The load verb is also ' \
114
+ 'used to import an extension or library at runtime.',
111
115
  :syntax => [
112
116
  'load {file_name}',
113
117
  'load ext {my_ext}',
@@ -116,11 +120,12 @@ module Gloo
116
120
  ],
117
121
  :parameters => [
118
122
  '{file_name} — Name of the object file that is to be loaded: ' \
119
- 'relative to the project, a full path, or starting with ~. ' \
123
+ 'a relative name (see the description for where it is ' \
124
+ 'looked for), a full path, or starting with ~. ' \
120
125
  'The .gloo extension is optional. ' \
121
- 'End it with * to load every .gloo file in a folder (a ' \
122
- 'folder in the project, a full path, or one starting with ' \
123
- '~); anything before the * is the start of the file names.',
126
+ 'End it with * to load every .gloo file in a folder, found ' \
127
+ 'the same way; the first place with matching files is ' \
128
+ 'used. Anything before the * is the start of the file names.',
124
129
  'reference type — By default, the type is file. File can be ' \
125
130
  'specified, but does not need to be. Use ext to load a ' \
126
131
  'User Extension. Use lib to load a Core Library.',
@@ -136,8 +141,8 @@ module Gloo
136
141
  "#{UNKNOWN_OPT_ERR} — The reference type given isn't file, ext, or lib.",
137
142
  "#{WRONG_NUM_ARGS_ERR} — load expects 2 or 3 arguments (the verb, an optional reference type, and the file/ext/lib name).",
138
143
  'File not found: {name} — The file name given could not be ' \
139
- 'resolved to a gloo file (checked as a full path, and ' \
140
- 'relative to the project and gloo root folders).'
144
+ 'resolved to a gloo file in any of the places it is ' \
145
+ 'looked for.'
141
146
  ],
142
147
  :notes => 'Several files can contribute to one container: ' \
143
148
  'declare it in each file and add different children, and ' \
@@ -156,7 +161,8 @@ module Gloo
156
161
  'is declared in more than one loaded file, the first ' \
157
162
  'non-empty doc wins, same as the first value.',
158
163
  :examples => <<~EXAMPLES.strip
159
- > load my/project/file
164
+ # Next to this file (or the app folder, in App mode)
165
+ > load parts/helpers
160
166
  > load my/app/*
161
167
  > load ~/.my_app/settings.gloo
162
168
  > load ~/.my_app/plugins/*