gloo 7.1.1 → 7.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d25bb60fb25bb1b42e44eeac62b0ec3bae86a9f3770c696caf4ab39b7f1c9784
4
- data.tar.gz: 260731b8d5f1cd1c6f3718be0d26f17d39139795e7e5c13ff8908a4fc3f1c453
3
+ metadata.gz: 153d674b150e06c999a0756c90c41f872a410205f516eaa8b560335f93ec0e2b
4
+ data.tar.gz: 1b11115e161f139b44dc6750713e0e883f638ae58048c8b3007441cbf681f319
5
5
  SHA512:
6
- metadata.gz: 3729efcd7dcfa5964a408fb28750bd2ac7caea3af323b1ea8c8b75503446f51805db7f20b05bfd705a9ac020bd06fe6fba5135013137e937f7dd6f499e134f40
7
- data.tar.gz: d69918fdaf8ab491ac5bc21cd7a838048ef4eaf2299d8a867eea017b14c0f5ca3a314e7106e6fc0bf00fad8e949550efaae897121d8ae11de4d964a1a0231218
6
+ metadata.gz: 3e92b508048653b4e1e4e283a7e90de32605b4a239f56b7b24123c600e5b38a6af582a50a6d2a7130da8ed73d8ded9f0e6aab2a75798a9709939749cf72119dd
7
+ data.tar.gz: 19cd40e0d35cfed856aa7eb6ffe4fe648adde3c706656d81211d01f0c940a4c38dd63b0bffe2a4b50b3b57d9e76afba997050c7fca99066f2fe6529825d05591
data/Rakefile CHANGED
@@ -2,8 +2,6 @@ require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new(:test) do |t|
5
- ENV[ 'GLOO_ENV' ] = 'TEST'
6
-
7
5
  t.warning = false
8
6
  t.libs << 'test'
9
7
  t.libs << 'lib'
data/docs/application.md CHANGED
@@ -31,6 +31,8 @@ gloo [global option] [file]
31
31
 
32
32
  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.
33
33
 
34
+ Gloo can also be started from another Ruby program; see Development, Running Gloo from Ruby.
35
+
34
36
  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 '{name}' was not found.` to stderr and exits with a non-zero status. Any other load or run error also exits non-zero — including syntax errors in a file that otherwise loaded (gloo keeps loading past them, reporting each with its file and line) — so a shell script calling gloo can tell a failed run from a clean one.
35
37
 
36
38
  When specifying a file there are a couple ways to reference the gloo file to open:
@@ -76,6 +78,11 @@ When specifying a file there are a couple ways to reference the gloo file to ope
76
78
  - Exits with status 1 if any test failed, or errors or warnings
77
79
  were logged that no test expected; 0 otherwise.
78
80
  - See Testing.
81
+ --embed - Run in Embedded mode
82
+ - Start the engine and return, for a Ruby program that drives
83
+ gloo itself.
84
+ - This is the default when gloo is created in code with no file
85
+ or mode. See Development, Running Gloo from Ruby.
79
86
  --version - Show application version
80
87
  --help - Show the help screen
81
88
  ```
data/docs/development.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Development
2
2
 
3
- How the interpreter's source is laid out, how to run its tests, and the conventions to follow when changing it. This page is for working on gloo itself; for using gloo, start with Getting Started.
3
+ How the interpreter's source is laid out, how to run its tests, the conventions to follow when changing it, and how to run gloo from your own Ruby code. This page is for working on gloo itself or driving it from Ruby; for using gloo, start with Getting Started.
4
4
 
5
5
  **Contents**
6
6
 
@@ -8,6 +8,7 @@ How the interpreter's source is laid out, how to run its tests, and the conventi
8
8
  - Tests
9
9
  - Documentation
10
10
  - Reporting Errors
11
+ - Running Gloo from Ruby
11
12
 
12
13
  ## Source Layout
13
14
 
@@ -40,7 +41,7 @@ There are two automated suites. Run both when making a change, and add to both:
40
41
 
41
42
  ### Unit tests — `test/`
42
43
 
43
- Standard Ruby minitest tests. The layout mirrors `lib/gloo/`, one test file per class or verb: `test/<area>/<name>_test.rb`, for example `test/verbs/show_test.rb` for `lib/gloo/verbs/show.rb`. Test classes inherit from `GlooTest` (in `test/base_test.rb`).
44
+ Standard Ruby minitest tests. The layout mirrors `lib/gloo/`, one test file per class or verb: `test/<area>/<name>_test.rb`, for example `test/verbs/show_test.rb` for `lib/gloo/verbs/show.rb`. Test classes inherit from `BaseTest` (in `test/base_test.rb`), or from `BaseEngineTest` (in `test/base_engine_test.rb`) for a test that needs a started engine in `@engine`.
44
45
 
45
46
  ```
46
47
  rake test
@@ -98,3 +99,22 @@ Gloo reports every problem in a way scripts can see (see Language, Syntax, Error
98
99
  - A question whose answer is "no" is just a result, not an error.
99
100
 
100
101
  Don't use `@engine.log.error` or `@engine.log.warn` for these; they only write to the log, and scripts can't see them. Build "not found" messages with `Gloo::Core::NotFound` (for example `NotFound.object( name )`) so they always read the same way.
102
+
103
+ ## Running Gloo from Ruby
104
+
105
+ Gloo can be started from another Ruby program, such as a test harness or an app that runs gloo commands. Create the engine with the params gloo should see — the same words you would type after `gloo` on the command line:
106
+
107
+ ```ruby
108
+ require 'gloo'
109
+
110
+ context = Gloo::App::EngineContext.new( [] )
111
+ engine = Gloo::App::Engine.new( context )
112
+ engine.start
113
+
114
+ engine.process_cmd "create greeting as string : 'hello'"
115
+ engine.process_cmd 'show greeting'
116
+ ```
117
+
118
+ Only those params are used. The Ruby program's own command line (`ARGV`) is never read, so running `ruby harness.rb some_file.gloo` doesn't make gloo load `some_file.gloo`. Pass anything gloo should see explicitly. Only the `gloo` command itself (`Gloo.run`) reads the command line.
119
+
120
+ With no file and no mode switch, the engine runs in Embedded mode (`--embed`): `start` sets the engine up and returns without loading or running anything, and your code drives it from there, for example with `process_cmd`.
data/docs/iterators.md CHANGED
@@ -9,7 +9,7 @@
9
9
  - Each Line
10
10
  - Each Word
11
11
  - Repeat
12
- - Ending a Loop Early
12
+ - Skipping an Item or Ending a Loop
13
13
 
14
14
  ## Each
15
15
 
@@ -215,7 +215,7 @@ Iterate for each line in a text block (string).
215
215
  Children:
216
216
 
217
217
  - `line` (string)
218
- - The single line of text.
218
+ - The single line of text, without its line ending (`\n` or `\r\n`). A blank line is still visited, as an empty string.
219
219
  - `in` (text)
220
220
  - The block of text or string.
221
221
  - `do` (script)
@@ -330,9 +330,26 @@ repeat [can] :
330
330
 
331
331
  See also: Object Base.
332
332
 
333
- ## Ending a Loop Early
333
+ ## Skipping an Item or Ending a Loop
334
334
 
335
- `break` in a loop's `do` script ends the whole loop, not just that pass. This works for `repeat` and every kind of `each`. The script that ran the loop carries on with its next line:
335
+ `break` ends the script it's written in. In a loop's `do` script, that skips the rest of this pass, and the loop goes on with the next item. This works for `repeat` and every kind of `each`:
336
+
337
+ ```gloo
338
+ skip [can] :
339
+ on_load [script] :
340
+ tell skip.r to run
341
+ show 'after the loop'
342
+ r [repeat] :
343
+ times [int] : 5
344
+ index [int] :
345
+ do [script] :
346
+ if skip.r.index = 2 then break
347
+ show skip.r.index
348
+ ```
349
+
350
+ This shows `0`, `1`, `3`, `4`, then `after the loop`.
351
+
352
+ `break loop` ends the whole loop. The script that ran the loop carries on with its next line:
336
353
 
337
354
  ```gloo
338
355
  early [can] :
@@ -343,12 +360,12 @@ early [can] :
343
360
  times [int] : 5
344
361
  index [int] :
345
362
  do [script] :
346
- if early.r.index = 2 then break
363
+ if early.r.index = 2 then break loop
347
364
  show early.r.index
348
365
  ```
349
366
 
350
367
  This shows `0`, `1`, then `after the loop`.
351
368
 
352
- `break` ends the script it's written in. So in nested loops, a `break` in the inner loop's `do` ends only the inner loop, and a `break` in a script the `do` script runs (with `run`, or a function it invokes) ends only that script, not the loop. An `each` with `group_by` still runs `on_group_end` for the group that was open when the loop ended.
369
+ `break loop` is only valid directly in a loop's `do` script. In nested loops, a `break loop` in the inner loop's `do` ends only the inner loop. Anywhere else — a script the `do` script runs (with `run`, or a function it invokes), a loop's `on_group_start` or `on_group_end`, or a script that isn't in a loop — it's an error (`Could not break loop: not in a loop.`) and nothing stops. To end a loop from a helper, have the helper set a flag and check it in the `do` script. An `each` with `group_by` still runs `on_group_end` for the group that was open when the loop ended.
353
370
 
354
371
  See also: Break.
data/docs/plugins.md CHANGED
@@ -372,7 +372,7 @@ Register it from `stats_ext.rb` with `callback.register_obj( Stats )`.
372
372
 
373
373
  ### Custom `each` iterators
374
374
 
375
- An extension's object can also plug into the built-in `each` verb by providing its own iterator. `extensions/git/src/each_repo.rb` shows the shape: a plain Ruby class (not a `Gloo::Core::Obj`) with `self.use_for?( iterator_obj )` — which returns `true` when the `each` loop's iterator object looks like a match (here, when it has a `repo` child) — and a `run` method that walks whatever it's iterating over, setting the loop's child value and calling `@iterator_obj.run_do` for each item. `run_do` returns true when a `break` in the `do` script ended it, so stop the walk then: `break if @iterator_obj.run_do`. See the git extension for the full pattern; this hook only makes sense for extensions that also register a matching object type (here, `git_repo`).
375
+ An extension's object can also plug into the built-in `each` verb by providing its own iterator. `extensions/git/src/each_repo.rb` shows the shape: a plain Ruby class (not a `Gloo::Core::Obj`) with `self.use_for?( iterator_obj )` — which returns `true` when the `each` loop's iterator object looks like a match (here, when it has a `repo` child) — and a `run` method that walks whatever it's iterating over, setting the loop's child value and calling `@iterator_obj.run_do` for each item. `run_do` returns true when a `break loop` in the `do` script ended it, so stop the walk then: `break if @iterator_obj.run_do`. (A plain `break` ends only that pass, and `run_do` returns false.) See the git extension for the full pattern; this hook only makes sense for extensions that also register a matching object type (here, `git_repo`).
376
376
 
377
377
  ### Documenting the extension
378
378
 
data/docs/verbs.md CHANGED
@@ -86,12 +86,13 @@ If the destination doesn't exist, or the expression can't be worked out (eg. it
86
86
 
87
87
  ## Load & Save
88
88
 
89
- `load` reads a `.gloo` file into the heap and runs its `on_load` script. Give a path relative to the project folder (no extension needed) or a full path (extension required); `*` in place of a file name loads every `.gloo` file in a folder.
89
+ `load` reads a `.gloo` file into the heap and runs its `on_load` script. Give a path relative to the project folder, a full path, or one starting with `~` for your home folder; the `.gloo` extension is optional. `*` in place of a file name loads every `.gloo` file in a folder, wherever the folder is. Anything before the `*` is the start of the file names, so `my/app/a*` loads just the files whose names start with `a`.
90
90
 
91
91
  ```gloo
92
92
  > load my/project/config
93
93
  > load my/app/*
94
94
  > load ~/.my_app/settings.gloo
95
+ > load ~/.my_app/plugins/*
95
96
  ```
96
97
 
97
98
  If the name can't be resolved to a file, `load` reports `File '{name}' was not found.` — 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.
data/lib/VERSION CHANGED
@@ -1 +1 @@
1
- 7.1.1
1
+ 7.1.3
data/lib/VERSION_NOTES CHANGED
@@ -1,3 +1,17 @@
1
+ 7.1.3 - 2026.10.29
2
+ - break in a loop's do skips to the next item again, as it did before 7.1.1.
3
+ - New: break loop ends the whole loop. It's only valid directly in a loop's do script.
4
+
5
+
6
+ 7.1.2 - 2026.10.28
7
+ - Fixes bug with create verb and ^.here reference.
8
+ - Behaviour change: an each over lines gives each line without its line ending.
9
+ - Load with * now works for a full path or a ~ folder, not just a folder in the project.
10
+ - Load no longer needs the .gloo extension for a full or ~ path.
11
+ - Clean run of gloo from a different ruby process (or test).
12
+ - Fixes looking for each child with a group by.
13
+
14
+
1
15
  7.1.1 - 2026.10.26
2
16
  - Fixes issues with the value in it after a failed command.
3
17
  - Warnings go to stderr, like errors.
data/lib/gloo/app/args.rb CHANGED
@@ -12,7 +12,6 @@ module Gloo
12
12
  class Args
13
13
 
14
14
  QUIET = 'quiet'.freeze
15
- GLOO_ENV = 'GLOO_ENV'.freeze
16
15
 
17
16
  attr_reader :switches, :files, :app_path, :command_tokens
18
17
 
@@ -26,8 +25,9 @@ module Gloo
26
25
  @app_path = nil
27
26
  @command_tokens = []
28
27
 
28
+ # Only the params given; the host process's ARGV is read by
29
+ # Gloo.run, when gloo is run as the command.
29
30
  params.each { |o| process_one_arg( o ) }
30
- ARGV.each { |o| process_one_arg( o ) }
31
31
  end
32
32
 
33
33
  #
@@ -117,9 +117,7 @@ module Gloo
117
117
  # Then finally use the default: embedded mode.
118
118
  #
119
119
  def detect_mode
120
- mode = if ENV[ GLOO_ENV ] == Gloo::App::Mode::TEST.to_s
121
- Mode::TEST
122
- elsif app?
120
+ mode = if app?
123
121
  Mode::APP
124
122
  elsif version?
125
123
  Mode::VERSION
data/lib/gloo/core/pn.rb CHANGED
@@ -172,7 +172,12 @@ module Gloo
172
172
  # returns nil without reporting an error. A caller that needs the
173
173
  # object reports its own error.
174
174
  #
175
+ # A here (^) or context (@) reference is expanded first, as resolve
176
+ # does, so a new object's parent can be given the same way as any
177
+ # other path (eg. create ^.x inside a script).
178
+ #
175
179
  def get_parent
180
+ expand_refs
176
181
  o = @engine.heap.root
177
182
 
178
183
  if self.includes_path?
@@ -16,6 +16,7 @@ module Gloo
16
16
  @engine = engine
17
17
  @obj = obj
18
18
  @break_out = false
19
+ @break_loop = false
19
20
  @line = 0
20
21
  end
21
22
 
@@ -59,10 +60,12 @@ module Gloo
59
60
  end
60
61
 
61
62
  #
62
- # Stop running this script.
63
+ # Stop running this script. With loop, also end the loop whose
64
+ # do script this is (break loop).
63
65
  #
64
- def break_out
66
+ def break_out( loop: false )
65
67
  @break_out = true
68
+ @break_loop = loop
66
69
  end
67
70
 
68
71
  #
@@ -72,6 +75,13 @@ module Gloo
72
75
  return @break_out
73
76
  end
74
77
 
78
+ #
79
+ # Did a break loop end this script's run, so its loop should end?
80
+ #
81
+ def broke_loop?
82
+ return @break_loop
83
+ end
84
+
75
85
  end
76
86
  end
77
87
  end
@@ -115,15 +115,15 @@ module Gloo
115
115
  def msg_run
116
116
  s = Gloo::Exec::Script.new( @engine, self )
117
117
  s.run
118
- @broke_out = s.broke_out?
118
+ @broke_loop = s.broke_loop?
119
119
  end
120
120
 
121
121
  #
122
- # Did a break end this script's last run? A loop uses this to
123
- # stop when its do script breaks.
122
+ # Did a break loop end this script's last run? A loop uses this
123
+ # to stop when its do script says break loop.
124
124
  #
125
- def broke_out?
126
- return @broke_out == true
125
+ def broke_loop?
126
+ return @broke_loop == true
127
127
  end
128
128
 
129
129
  # ---------------------------------------------------------------------
@@ -43,15 +43,16 @@ module Gloo
43
43
  end
44
44
 
45
45
  #
46
- # Run the do script once. Returns true if a break in it ended the
47
- # run, so the loop should stop.
46
+ # Run the do script once. Returns true if a break loop in it
47
+ # ended the run, so the loop should stop. A plain break ends only
48
+ # the do script, and the loop goes on.
48
49
  #
49
50
  def run_do
50
51
  o = find_child DO
51
52
  return false unless o
52
53
 
53
54
  Gloo::Exec::Dispatch.message( @engine, 'run', o )
54
- return o.respond_to?( :broke_out? ) && o.broke_out?
55
+ return o.respond_to?( :broke_loop? ) && o.broke_loop?
55
56
  end
56
57
 
57
58
 
@@ -134,7 +135,7 @@ module Gloo
134
135
  'One of the following, whose presence determines which kind of loop this is:',
135
136
  'child (alias) — iterate a container\'s children.',
136
137
  'word (string) — iterate the words in a string.',
137
- 'line (string) — iterate the lines in a block of text.',
138
+ 'line (string) — iterate the lines in a block of text. Each line comes without its line ending; a blank line is visited as an empty string.',
138
139
  'file (file) — iterate the files in a folder. Optional children: ext (string) limits to files of that extension, ignoring case; recursive (bool) walks the subfolders too; include_dirs (bool) lists folders as well as files.',
139
140
  'dir (file) — iterate the directories in a folder. Optional child: recursive (bool) walks the subfolders too.'
140
141
  ],
@@ -116,20 +116,24 @@ module Gloo
116
116
 
117
117
  o = Gloo::Objs::Alias.resolve_alias( @engine, o )
118
118
  o.children.each do |child|
119
+ new_group = false
119
120
  if group_mode
120
121
  group_value = group_by_value( child )
121
122
  if last_group != group_value
122
123
  last_group = group_value
124
+ new_group = true
125
+ # The ending group's end runs while child is still its last child.
123
126
  if first_time
124
127
  first_time = false
125
128
  else
126
129
  run_on_group_end
127
130
  end
128
- run_on_group_start
129
131
  end
130
132
  end
131
133
 
132
134
  set_child child
135
+ # The new group's start runs once child is its first child.
136
+ run_on_group_start if new_group
133
137
  break if @iterator_obj.run_do
134
138
  end
135
139
 
@@ -40,13 +40,14 @@ module Gloo
40
40
  # ---------------------------------------------------------------------
41
41
 
42
42
  #
43
- # Run for each line.
43
+ # Run for each line, without its line ending (\n or \r\n). A blank
44
+ # line is still visited, as an empty string.
44
45
  #
45
46
  def run
46
47
  str = @iterator_obj.in_value
47
48
  return unless str
48
49
 
49
- str.each_line do |line|
50
+ str.each_line( chomp: true ) do |line|
50
51
  set_line line
51
52
  break if @iterator_obj.run_do
52
53
  end
@@ -37,15 +37,16 @@ module Gloo
37
37
  end
38
38
 
39
39
  #
40
- # Run the do script once. Returns true if a break in it ended the
41
- # run, so the loop should stop.
40
+ # Run the do script once. Returns true if a break loop in it
41
+ # ended the run, so the loop should stop. A plain break ends only
42
+ # the do script, and the loop goes on.
42
43
  #
43
44
  def run_do
44
45
  o = find_child DO
45
46
  return false unless o
46
47
 
47
48
  Gloo::Exec::Dispatch.message( @engine, 'run', o )
48
- return o.respond_to?( :broke_out? ) && o.broke_out?
49
+ return o.respond_to?( :broke_loop? ) && o.broke_loop?
49
50
  end
50
51
 
51
52
  #
@@ -28,22 +28,30 @@ module Gloo
28
28
  end
29
29
 
30
30
  #
31
- # Get all the gloo files in the folder (partial path).
31
+ # Get all the gloo files in the folder (partial path). The folder
32
+ # can end with the start of a file name, as a prefix (folder/a).
32
33
  #
33
34
  def get_all_files_in( folder )
34
- pns = []
35
- dir = File.join( @engine.settings.project_path, folder )
35
+ dir = folder_for_files( folder )
36
+ return Dir.glob( "#{dir}*.gloo" )
37
+ end
36
38
 
37
- unless Dir.exist?( dir )
38
- @log.debug "Folder does not exist in project: #{folder}"
39
- dir = File.join( @engine.settings.user_root, folder )
40
- @log.debug "Looking in gloo home? found == #{Dir.exist?( dir )}"
41
- end
39
+ #
40
+ # Where to look for a folder of files: an absolute or ~ folder as
41
+ # given (~ expanded), anything else in the project folder, or if it
42
+ # isn't there, in the gloo home.
43
+ #
44
+ def folder_for_files( folder )
45
+ return folder.sub( '~', Dir.home ) if folder.start_with?( '~' )
46
+ return folder if File.absolute_path?( folder )
42
47
 
43
- Dir.glob( "#{dir}*.gloo" ).each do |f|
44
- pns << f
45
- end
46
- return pns
48
+ dir = File.join( @engine.settings.project_path, folder )
49
+ return dir if Dir.exist?( dir )
50
+
51
+ @log.debug "Folder does not exist in project: #{folder}"
52
+ dir = File.join( @engine.settings.user_root, folder )
53
+ @log.debug "Looking in gloo home? found == #{Dir.exist?( dir )}"
54
+ return dir
47
55
  end
48
56
 
49
57
  #
@@ -65,23 +73,33 @@ module Gloo
65
73
  return true
66
74
  end
67
75
 
68
- #
69
- # Expand a single file path.
70
- #
76
+ #
77
+ # Expand a single file path. The .gloo extension is optional: it's
78
+ # added when the name doesn't end with it, for a full or ~ path
79
+ # and for a name in the project or gloo home.
80
+ #
71
81
  def expand( name )
72
- # Try full path
82
+ # Try the name as given: a full path, or relative to the working
83
+ # folder (eg. gloo hello_world.gloo).
73
84
  ext_path = File.expand_path( name )
74
85
  return [ ext_path ] if self.valid?( ext_path )
75
86
 
76
- # Try in user root, projects
77
- full_name = "#{name}#{file_ext}"
87
+ full_name = name.end_with?( file_ext ) ? name : "#{name}#{file_ext}"
88
+
89
+ # A full or ~ path without the extension.
90
+ if name.start_with?( '~' ) || File.absolute_path?( name )
91
+ ext_path = File.expand_path( full_name )
92
+ return [ ext_path ] if self.valid?( ext_path )
93
+ end
94
+
95
+ # Try in projects
78
96
  pn = File.join( @engine.settings.project_path, full_name )
79
97
  return [ pn ] if self.valid?( pn )
80
-
98
+
81
99
  # Try in user root
82
100
  pn = File.join( @engine.settings.user_root, full_name )
83
101
  return [ pn ] if self.valid?( pn )
84
-
102
+
85
103
  return nil
86
104
  end
87
105
 
@@ -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
@@ -111,7 +111,9 @@ module Gloo
111
111
  'value. Both type and value are optional when creating an object.',
112
112
  :syntax => [ 'create {new.object.path} as {type} : {value}' ],
113
113
  :parameters => [
114
- '{new.object.path} — The path and name of the new object.',
114
+ '{new.object.path} — The path and name of the new object. ' \
115
+ 'Like any path, it can start from the root, from here in ' \
116
+ 'a script (^.box.new, ^^.new), or from the context (@.new).',
115
117
  '{type} — The type of the new object. Optional; if not provided the object will be untyped.',
116
118
  "{value} — The initial value for the new object. Optional; if not provided the object will have the default value for the type."
117
119
  ],
@@ -123,7 +125,7 @@ module Gloo
123
125
  "to the existing object's current value.",
124
126
  :errors => [
125
127
  "#{NO_NAME_ERR} — The name of the object was not specified and the object cannot be created.",
126
- "Could not create '{path}': Object '{parent.path}' was not found. — 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. Nothing is created, and it is false.",
128
+ "Could not create '{path}': Object '{parent.path}' was not found. — The parent container named in the path does not exist. Every container above the new object must already exist. Nothing is created, and it is false.",
127
129
  "#{format( BAD_NAME_ERR, '{name}' )} — The name has parentheses or quotes, or there are extra words between the name and as / : — create takes a name as written. Nothing is created, and it is false."
128
130
  ],
129
131
  :examples => <<~EXAMPLES.strip
@@ -115,7 +115,12 @@ module Gloo
115
115
  'load [ext|lib] ( {expression} )'
116
116
  ],
117
117
  :parameters => [
118
- '{file_name} — Name of the object file that is to be loaded.',
118
+ '{file_name} — Name of the object file that is to be loaded: ' \
119
+ 'relative to the project, a full path, or starting with ~. ' \
120
+ '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.',
119
124
  'reference type — By default, the type is file. File can be ' \
120
125
  'specified, but does not need to be. Use ext to load a ' \
121
126
  'User Extension. Use lib to load a Core Library.',
@@ -154,6 +159,7 @@ module Gloo
154
159
  > load my/project/file
155
160
  > load my/app/*
156
161
  > load ~/.my_app/settings.gloo
162
+ > load ~/.my_app/plugins/*
157
163
 
158
164
  # Load a file whose name is worked out
159
165
  > load ( $.gloo_tmp '/scratch.gloo' )
data/lib/gloo.rb CHANGED
@@ -9,13 +9,27 @@ path = File.dirname( File.absolute_path( __FILE__ ) )
9
9
  require File.join( path, 'dependencies.rb' )
10
10
 
11
11
  module Gloo
12
- def self.run
13
- params = []
14
- ( params << '--cli' ) if ARGV.count.zero?
15
12
 
13
+ #
14
+ # Run gloo as the command, with the command line's arguments.
15
+ # This is the only place that reads ARGV; an engine created
16
+ # in code uses only the params it's given.
17
+ #
18
+ def self.run( argv = ARGV )
16
19
  platform = Gloo::App::Platform.new
17
20
  context = Gloo::App::EngineContext.new(
18
- params, platform, Gloo::App::Log, nil )
21
+ params_for( argv ), platform, Gloo::App::Log, nil )
19
22
  Gloo::App::Engine.new( context ).start
20
23
  end
24
+
25
+ #
26
+ # Get the engine params for the given command line arguments.
27
+ # With no arguments, gloo runs in CLI mode.
28
+ #
29
+ def self.params_for( argv )
30
+ params = argv.dup
31
+ ( params << '--cli' ) if params.empty?
32
+ return params
33
+ end
34
+
21
35
  end
data/lib/run.rb CHANGED
@@ -5,14 +5,8 @@
5
5
  # From the /lib/ directory: ruby run.rb
6
6
  #
7
7
 
8
- # Look for all required dependencies.
8
+ # Load gloo from source, then run it with the command line's arguments.
9
9
  path = File.dirname( File.absolute_path( __FILE__ ) )
10
- require File.join( path, 'dependencies.rb' )
10
+ require File.join( path, 'gloo.rb' )
11
11
 
12
- params = []
13
- ( params << '--cli' ) if ARGV.count.zero?
14
-
15
- platform = Gloo::App::Platform.new
16
- context = Gloo::App::EngineContext.new(
17
- params, platform, Gloo::App::Log, nil )
18
- Gloo::App::Engine.new( context ).start
12
+ Gloo.run
@@ -0,0 +1,31 @@
1
+ #
2
+ # Embedded gloo tests.
3
+ # An engine created in code by another Ruby program uses only the
4
+ # params it's given, never that program's command line (ARGV).
5
+ # The engine running these tests was started as the command, so the
6
+ # check runs a separate Ruby program (a fixture) with [system].
7
+ #
8
+
9
+ tests.app.embed [can] :
10
+
11
+ # The harness, beside this file; found with source_dir.
12
+ harness [string] : /fixtures/embed_harness.rb
13
+ host_arg [string] : host_arg.gloo
14
+ no_files [string] : 0
15
+ where [string] :
16
+
17
+ run_harness [system] :
18
+ command [string] :
19
+ get_output [bool] : true
20
+ result [string] :
21
+
22
+ ignores_host_argv [test] :
23
+ description [string] : An engine created in code ignores the host program's command line.
24
+ on_test [script] :
25
+ check ^^ for source_dir
26
+ put it into ^^.where
27
+ # Paths are quoted in case the folder has spaces.
28
+ put "ruby '" ^^.where ^^.harness "' " ^^.host_arg into ^^.run_harness.command
29
+ tell ^^.run_harness to run
30
+ eval ^^.run_harness.result = ^^.no_files
31
+ assert "expected the embedded engine to be given no files"
@@ -0,0 +1,15 @@
1
+ #
2
+ # A Ruby program that creates a gloo engine in code, as an embedding
3
+ # app would, and prints how many files the engine was given.
4
+ #
5
+ # Run it with words on its own command line: the engine must not
6
+ # take them as files, so this should always print 0. The engine is
7
+ # never started, so nothing is loaded or saved even if it does.
8
+ #
9
+ # Requires this repo's lib/gloo by path, so it checks the source,
10
+ # not an installed gloo gem.
11
+ #
12
+ require_relative '../../../lib/gloo'
13
+
14
+ context = Gloo::App::EngineContext.new( [ '--quiet' ] )
15
+ print Gloo::App::Engine.new( context ).args.files.count
@@ -20,6 +20,27 @@ tests.ctrl.each [can] :
20
20
  eval ^^.count + 1
21
21
  put it into ^^.count
22
22
 
23
+ seen [string] :
24
+ lines [text] : BEGIN
25
+ one
26
+
27
+ two
28
+ END
29
+
30
+ lns [each] :
31
+ line [string] :
32
+ in [alias] : tests.ctrl.each.lines
33
+ do [script] :
34
+ put tests.ctrl.each.seen + '[' + ^.line + ']' into tests.ctrl.each.seen
35
+
36
+ each_line_without_line_endings [test] :
37
+ description [string] : each line gives each line without its line ending; a blank line is empty.
38
+ on_test [script] :
39
+ put '' into ^^.seen
40
+ tell ^^.lns to run
41
+ eval ^^.seen = '[one][][two]'
42
+ assert "expected each line without its line ending"
43
+
23
44
  each_word [test] :
24
45
  description [string] : Count words with each word
25
46
  on_test [script] :
@@ -137,3 +158,54 @@ tests.ctrl.each_folder [can] :
137
158
  eval ^^.count = 2
138
159
  assert "expected a and a/deep"
139
160
  tell ^^.remove_tree to run
161
+
162
+ #
163
+ # Each child with group_by: on_group_start sees the group's first
164
+ # child and on_group_end its last. Each start and end logs the id of
165
+ # the child it sees.
166
+ #
167
+
168
+ tests.ctrl.each_group [can] :
169
+
170
+ starts [string] :
171
+ ends [string] :
172
+
173
+ items [can] :
174
+ a [can] :
175
+ id [string] : a
176
+ kind [string] : x
177
+ b [can] :
178
+ id [string] : b
179
+ kind [string] : x
180
+ c [can] :
181
+ id [string] : c
182
+ kind [string] : y
183
+ d [can] :
184
+ id [string] : d
185
+ kind [string] : y
186
+
187
+ grouped [each] :
188
+ child [alias] :
189
+ in [alias] : tests.ctrl.each_group.items
190
+ group_by [string] : kind
191
+ on_group_start [script] :
192
+ put tests.ctrl.each_group.starts + tests.ctrl.each_group.grouped.child.id into tests.ctrl.each_group.starts
193
+ on_group_end [script] :
194
+ put tests.ctrl.each_group.ends + tests.ctrl.each_group.grouped.child.id into tests.ctrl.each_group.ends
195
+ do [script] :
196
+
197
+ group_start_sees_first_child [test] :
198
+ description [string] : on_group_start sees the first child of the new group.
199
+ on_test [script] :
200
+ put '' into ^^.starts
201
+ tell ^^.grouped to run
202
+ eval ^^.starts = 'ac'
203
+ assert "expected each group to start with its first child"
204
+
205
+ group_end_sees_last_child [test] :
206
+ description [string] : on_group_end sees the last child of the group that's ending.
207
+ on_test [script] :
208
+ put '' into ^^.ends
209
+ tell ^^.grouped to run
210
+ eval ^^.ends = 'bd'
211
+ assert "expected each group to end with its last child"
@@ -8,7 +8,7 @@ tests.verbs.break [can] :
8
8
 
9
9
  s [script] :
10
10
  if ^.b then break
11
-
11
+
12
12
  # Should not get here
13
13
  put false into ^.b
14
14
 
@@ -19,16 +19,27 @@ tests.verbs.break [can] :
19
19
  eval ^^.b
20
20
  assert "expected b to be true"
21
21
 
22
- # Loops whose do script breaks on its second pass.
22
+ # The last error, for the break loop errors.
23
+ on_error [script] :
24
+ error_data [can] :
25
+ message [string] :
26
+
27
+ not_in_a_loop [string] : Could not break loop: not in a loop.
28
+
29
+ # Loops whose do script breaks on one pass: skip_on for a plain
30
+ # break, end_on for break loop (0 never breaks).
23
31
  passes [int] : 0
24
32
  after [int] : 0
33
+ skip_on [int] : 0
34
+ end_on [int] : 0
25
35
 
26
36
  rep [repeat] :
27
37
  times [int] : 4
28
38
  index [int] :
29
39
  do [script] :
30
40
  put tests.verbs.break.passes + 1 into tests.verbs.break.passes
31
- if tests.verbs.break.passes = 2 then break
41
+ if tests.verbs.break.passes = tests.verbs.break.skip_on then break
42
+ if tests.verbs.break.passes = tests.verbs.break.end_on then break loop
32
43
  put tests.verbs.break.after + 1 into tests.verbs.break.after
33
44
 
34
45
  kids [can] :
@@ -41,14 +52,15 @@ tests.verbs.break [can] :
41
52
  in [alias] : tests.verbs.break.kids
42
53
  do [script] :
43
54
  put tests.verbs.break.passes + 1 into tests.verbs.break.passes
44
- if tests.verbs.break.passes = 2 then break
55
+ if tests.verbs.break.passes = tests.verbs.break.skip_on then break
56
+ if tests.verbs.break.passes = tests.verbs.break.end_on then break loop
45
57
  put tests.verbs.break.after + 1 into tests.verbs.break.after
46
58
 
47
59
  inner [repeat] :
48
60
  times [int] : 3
49
61
  index [int] :
50
62
  do [script] :
51
- if tests.verbs.break.inner.index = 1 then break
63
+ if tests.verbs.break.inner.index = 1 then break loop
52
64
  put tests.verbs.break.after + 1 into tests.verbs.break.after
53
65
 
54
66
  outer [repeat] :
@@ -57,30 +69,111 @@ tests.verbs.break [can] :
57
69
  do [script] :
58
70
  tell tests.verbs.break.inner to run
59
71
 
60
- break_ends_a_repeat [test] :
61
- description [string] : break in a repeat's do script ends the loop.
72
+ # The form the apps use to skip an item: unless ... do break.
73
+ skipped [string] :
74
+ skip_b [each] :
75
+ child [alias] :
76
+ in [alias] : tests.verbs.break.kids
77
+ do [script] :
78
+ check tests.verbs.break.skip_b.child for starts_with?( 'b' )
79
+ unless it = false do break
80
+ put tests.verbs.break.skipped + tests.verbs.break.skip_b.child into tests.verbs.break.skipped
81
+
82
+ # break loop is only valid directly in a loop's do script.
83
+ lone [script] :
84
+ break loop
85
+
86
+ loop_helper [script] :
87
+ break loop
88
+
89
+ via_loop [repeat] :
90
+ times [int] : 2
91
+ index [int] :
92
+ do [script] :
93
+ put tests.verbs.break.passes + 1 into tests.verbs.break.passes
94
+ run tests.verbs.break.loop_helper
95
+
96
+ break_skips_the_rest_of_a_repeat_pass [test] :
97
+ description [string] : break in a repeat's do script skips to the next pass.
62
98
  on_test [script] :
63
99
  put 0 into ^^.passes
64
100
  put 0 into ^^.after
101
+ put 0 into ^^.end_on
102
+ put 2 into ^^.skip_on
103
+ tell ^^.rep to run
104
+ eval ^^.passes = 4
105
+ assert "expected every pass to run"
106
+ eval ^^.after = 3
107
+ assert "expected only the pass that broke to stop early"
108
+
109
+ break_skips_the_rest_of_an_each_pass [test] :
110
+ description [string] : break in an each's do script skips to the next child.
111
+ on_test [script] :
112
+ put 0 into ^^.passes
113
+ put 0 into ^^.after
114
+ put 0 into ^^.end_on
115
+ put 2 into ^^.skip_on
116
+ tell ^^.each_kid to run
117
+ eval ^^.after = 2
118
+ assert "expected every child but the one that broke to get past it"
119
+
120
+ unless_do_break_skips_an_item [test] :
121
+ description [string] : unless ... do break in an each skips that item, as the apps use it.
122
+ on_test [script] :
123
+ put '' into ^^.skipped
124
+ tell ^^.skip_b to run
125
+ eval ^^.skipped = 'ac'
126
+ assert "expected b to be skipped and c still reached"
127
+
128
+ break_loop_ends_a_repeat [test] :
129
+ description [string] : break loop in a repeat's do script ends the loop.
130
+ on_test [script] :
131
+ put 0 into ^^.passes
132
+ put 0 into ^^.after
133
+ put 0 into ^^.skip_on
134
+ put 2 into ^^.end_on
65
135
  tell ^^.rep to run
66
136
  eval ^^.passes = 2
67
137
  assert "expected the loop to stop on the pass that broke"
68
138
  eval ^^.after = 1
69
139
  assert "expected only the first pass to get past the break"
70
140
 
71
- break_ends_an_each [test] :
72
- description [string] : break in an each's do script ends the loop.
141
+ break_loop_ends_an_each [test] :
142
+ description [string] : break loop in an each's do script ends the loop.
73
143
  on_test [script] :
74
144
  put 0 into ^^.passes
75
145
  put 0 into ^^.after
146
+ put 0 into ^^.skip_on
147
+ put 2 into ^^.end_on
76
148
  tell ^^.each_kid to run
77
149
  eval ^^.passes = 2
78
150
  assert "expected the loop to stop on the pass that broke"
79
151
 
80
- break_ends_only_the_inner_loop [test] :
81
- description [string] : break in an inner loop ends that loop, not the outer one.
152
+ break_loop_ends_only_the_inner_loop [test] :
153
+ description [string] : break loop in an inner loop ends that loop, not the outer one.
82
154
  on_test [script] :
83
155
  put 0 into ^^.after
84
156
  tell ^^.outer to run
85
157
  eval ^^.after = 2
86
158
  assert "expected each of the 2 inner loops to get past its break once"
159
+
160
+ break_loop_outside_a_loop_is_an_error [test] :
161
+ description [string] : break loop in a script that isn't a loop's do is an error.
162
+ expect_errors [bool] : true
163
+ on_test [script] :
164
+ put '' into ^^.error_data.message
165
+ run ^^.lone
166
+ eval ^^.error_data.message = ^^.not_in_a_loop
167
+ assert "expected the not-in-a-loop error"
168
+
169
+ break_loop_in_a_helper_is_an_error [test] :
170
+ description [string] : break loop in a script the do runs is an error, and the loop goes on.
171
+ expect_errors [bool] : true
172
+ on_test [script] :
173
+ put '' into ^^.error_data.message
174
+ put 0 into ^^.passes
175
+ tell ^^.via_loop to run
176
+ eval ^^.error_data.message = ^^.not_in_a_loop
177
+ assert "expected the not-in-a-loop error"
178
+ eval ^^.passes = 2
179
+ assert "expected the loop to go on"
@@ -11,6 +11,31 @@ tests.verbs.create [can] :
11
11
  eval it = 'hello'
12
12
  assert "expected it to equal the created value"
13
13
 
14
+ here_box [can] :
15
+
16
+ create_with_a_here_path [test] :
17
+ description [string] : create takes a ^ path inside a script, like other verbs.
18
+ on_test [script] :
19
+ create ^^.here_box.made as string : 'made here'
20
+ eval tests.verbs.create.here_box.made = 'made here'
21
+ assert "expected ^^.here_box.made to be created"
22
+
23
+ create_with_a_one_level_here_path [test] :
24
+ description [string] : create ^.name makes a child of the script's parent (here, the test).
25
+ on_test [script] :
26
+ create ^.child_of_test as int : 3
27
+ eval tests.verbs.create.create_with_a_one_level_here_path.child_of_test = 3
28
+ assert "expected ^.child_of_test under the test"
29
+
30
+ create_with_a_context_path [test] :
31
+ description [string] : create takes an @ path, relative to the context.
32
+ on_test [script] :
33
+ context tests.verbs.create.here_box
34
+ create @.via_context as int : 5
35
+ context root
36
+ eval tests.verbs.create.here_box.via_context = 5
37
+ assert "expected @.via_context to be created in the context"
38
+
14
39
  tests.verbs.create_names [can] :
15
40
 
16
41
  err_fired [bool] : false
@@ -13,6 +13,9 @@ tests.verbs.load [can] :
13
13
  # Scratch files live in the gloo tmp folder ($.gloo_tmp).
14
14
  on_load [script] :
15
15
  put $.gloo_tmp '/gloo_load_no_objects_test.gloo' into ^.f
16
+ put $.gloo_tmp ^.wild_name into ^.wild_dir
17
+ put ^.wild_dir '/wild_a.gloo' into ^.wild_a
18
+ put ^.wild_dir '/wild_b.gloo' into ^.wild_b
16
19
 
17
20
  comments_only [text] : BEGIN
18
21
  # This file has comments,
@@ -26,6 +29,10 @@ END
26
29
 
27
30
  computed_name [string] : /gloo_load_computed_test.gloo
28
31
  lib_name [string] : md
32
+ wild_name [string] : /gloo_load_wild_test
33
+ wild_dir [file] :
34
+ wild_a [file] :
35
+ wild_b [file] :
29
36
  computed_f [file] :
30
37
  computed_fixture [text] : BEGIN
31
38
  computed_demo [can] :
@@ -138,3 +145,30 @@ END
138
145
  assert "expected load lib ( expr ) not to fire on_error"
139
146
  exists? object markdown
140
147
  assert "expected the markdown type to be registered"
148
+
149
+ load_an_absolute_wildcard_path [test] :
150
+ description [string] : load {absolute folder}/* loads every .gloo file in the folder.
151
+ on_test [script] :
152
+ tell ^^.wild_dir to mkdir
153
+ tell ^^.wild_a to write ( 'wild_a [can] :' )
154
+ tell ^^.wild_b to write ( 'wild_b [can] :' )
155
+ load ( ^^.wild_dir '/*' )
156
+ exists? wild_a
157
+ assert "expected wild_a.gloo to be loaded"
158
+ exists? wild_b
159
+ assert "expected wild_b.gloo to be loaded"
160
+ tell wild_a to unload
161
+ tell wild_b to unload
162
+ tell ^^.wild_a to delete
163
+ tell ^^.wild_b to delete
164
+
165
+ load_a_full_path_without_the_extension [test] :
166
+ description [string] : load of a full path finds the .gloo file without the extension being given.
167
+ on_test [script] :
168
+ put $.gloo_tmp ^^.computed_name into ^^.computed_f
169
+ tell ^^.computed_f to write ( ^^.computed_fixture )
170
+ load ( $.gloo_tmp '/gloo_load_computed_test' )
171
+ eval computed_demo.s = 'hi'
172
+ assert "expected the full path without .gloo to load"
173
+ tell computed_demo to unload
174
+ tell ^^.computed_f to delete
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: 7.1.1
4
+ version: 7.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
@@ -442,6 +442,8 @@ files:
442
442
  - lib/gloo/verbs/version.rb
443
443
  - lib/gloo/verbs/wait.rb
444
444
  - lib/run.rb
445
+ - test.gloo/app/embed.test.gloo
446
+ - test.gloo/app/fixtures/embed_harness.rb
445
447
  - test.gloo/basic.test.gloo
446
448
  - test.gloo/ctrl/each.test.gloo
447
449
  - test.gloo/dt/date.test.gloo