gloo 6.7.2 → 7.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 +1 -0
- data/docs/application.md +3 -1
- data/docs/iterators.md +39 -3
- data/docs/language_syntax.md +53 -5
- data/docs/objects.md +58 -1
- data/docs/operators.md +16 -0
- data/docs/plugins.md +9 -1
- data/docs/verbs.md +4 -2
- data/lib/VERSION +1 -1
- data/lib/VERSION_NOTES +20 -0
- data/lib/gloo/app/engine.rb +64 -8
- data/lib/gloo/app/log.rb +47 -6
- data/lib/gloo/convert/converter.rb +63 -2
- data/lib/gloo/convert/string_to_date.rb +14 -0
- data/lib/gloo/convert/string_to_datetime.rb +16 -0
- data/lib/gloo/convert/string_to_decimal.rb +13 -0
- data/lib/gloo/convert/string_to_integer.rb +9 -2
- data/lib/gloo/convert/string_to_time.rb +28 -0
- data/lib/gloo/core/error.rb +11 -2
- data/lib/gloo/core/event_manager.rb +7 -4
- data/lib/gloo/core/factory.rb +16 -11
- data/lib/gloo/core/here.rb +6 -1
- data/lib/gloo/core/invoker.rb +2 -3
- data/lib/gloo/core/not_found.rb +42 -0
- data/lib/gloo/core/obj.rb +28 -2
- data/lib/gloo/core/op.rb +2 -0
- data/lib/gloo/core/parser.rb +1 -1
- data/lib/gloo/core/pn.rb +33 -12
- data/lib/gloo/core/tokens.rb +38 -0
- data/lib/gloo/core/verb.rb +54 -0
- data/lib/gloo/exec/dispatch.rb +5 -6
- data/lib/gloo/exec/exec_env.rb +13 -0
- data/lib/gloo/exec/runner.rb +10 -2
- data/lib/gloo/exec/script.rb +13 -1
- data/lib/gloo/expr/expression.rb +39 -1
- data/lib/gloo/expr/op_plus.rb +3 -0
- data/lib/gloo/objs/basic/boolean.rb +16 -0
- data/lib/gloo/objs/basic/container.rb +154 -6
- data/lib/gloo/objs/basic/decimal.rb +1 -0
- data/lib/gloo/objs/basic/integer.rb +1 -0
- data/lib/gloo/objs/basic/string.rb +0 -1
- data/lib/gloo/objs/basic/string_msgs.rb +68 -32
- data/lib/gloo/objs/ctrl/each.rb +22 -3
- data/lib/gloo/objs/ctrl/each_dir.rb +20 -7
- data/lib/gloo/objs/ctrl/each_file.rb +78 -9
- data/lib/gloo/objs/ctrl/function.rb +6 -5
- data/lib/gloo/objs/dt/date.rb +28 -11
- data/lib/gloo/objs/dt/datetime.rb +72 -21
- data/lib/gloo/objs/dt/dt_tools.rb +46 -2
- data/lib/gloo/objs/dt/time.rb +28 -11
- data/lib/gloo/objs/str_utils/cipher.rb +17 -4
- data/lib/gloo/objs/str_utils/password.rb +6 -2
- data/lib/gloo/objs/system/erb.rb +9 -3
- data/lib/gloo/objs/system/file_handle.rb +57 -21
- data/lib/gloo/objs/system/system.rb +6 -2
- data/lib/gloo/objs/web/http_get.rb +12 -2
- data/lib/gloo/objs/web/http_post.rb +6 -2
- data/lib/gloo/objs/web/json.rb +38 -18
- data/lib/gloo/objs/web/uri.rb +36 -16
- data/lib/gloo/persist/file_loader.rb +72 -11
- data/lib/gloo/persist/file_saver.rb +2 -1
- data/lib/gloo/persist/indent_stack.rb +49 -33
- data/lib/gloo/persist/line_splitter.rb +40 -3
- data/lib/gloo/persist/persist_man.rb +2 -4
- data/lib/gloo/plugin/ext_manager.rb +6 -9
- data/lib/gloo/plugin/lib_manager.rb +2 -2
- data/lib/gloo/verbs/break.rb +1 -0
- data/lib/gloo/verbs/check.rb +1 -1
- data/lib/gloo/verbs/cls.rb +1 -0
- data/lib/gloo/verbs/create.rb +32 -4
- data/lib/gloo/verbs/execute.rb +1 -1
- data/lib/gloo/verbs/exists.rb +27 -15
- data/lib/gloo/verbs/files.rb +2 -1
- data/lib/gloo/verbs/help.rb +1 -0
- data/lib/gloo/verbs/if.rb +1 -1
- data/lib/gloo/verbs/invoke.rb +1 -1
- data/lib/gloo/verbs/list.rb +4 -3
- data/lib/gloo/verbs/load.rb +3 -3
- data/lib/gloo/verbs/move.rb +10 -8
- data/lib/gloo/verbs/put.rb +20 -8
- data/lib/gloo/verbs/quit.rb +1 -0
- data/lib/gloo/verbs/redirect.rb +8 -2
- data/lib/gloo/verbs/reload.rb +9 -1
- data/lib/gloo/verbs/run.rb +2 -2
- data/lib/gloo/verbs/save.rb +4 -2
- data/lib/gloo/verbs/tell.rb +1 -1
- data/lib/gloo/verbs/unless.rb +1 -1
- data/lib/gloo/verbs/unload.rb +8 -0
- data/test.gloo/ctrl/each.test.gloo +88 -0
- data/test.gloo/lang/declaration.test.gloo +27 -0
- data/test.gloo/lang/exceptions.test.gloo +60 -0
- data/test.gloo/lang/failures.test.gloo +139 -0
- data/test.gloo/lang/it.test.gloo +133 -0
- data/test.gloo/lang/naming.test.gloo +11 -0
- data/test.gloo/lang/ops.test.gloo +30 -0
- data/test.gloo/objs/can.test.gloo +101 -0
- data/test.gloo/objs/string.test.gloo +24 -0
- data/test.gloo/string/str.test.gloo +28 -0
- data/test.gloo/verbs/create.test.gloo +36 -0
- data/test.gloo/verbs/exists.test.gloo +33 -1
- data/test.gloo/verbs/unload.test.gloo +34 -0
- metadata +4 -1
|
@@ -10,6 +10,8 @@ module Gloo
|
|
|
10
10
|
module Objs
|
|
11
11
|
module StringMsgs
|
|
12
12
|
|
|
13
|
+
MISSING_PARAM_MSG = 'Missing parameter!'.freeze
|
|
14
|
+
|
|
13
15
|
#
|
|
14
16
|
# Get the list of message names this mixin implements, derived
|
|
15
17
|
# from its own msg_* methods so String/Text don't have to
|
|
@@ -39,7 +41,8 @@ module Gloo
|
|
|
39
41
|
'starts_with? ({str}) — Check if the string starts with the given string. A parameter is required: the string to look for at the beginning of this string. It will have a boolean.',
|
|
40
42
|
'ends_with? ({str}) — Check if the string ends with the given string. A parameter is required: the string to look for at the end of this string. It will have a boolean.',
|
|
41
43
|
'substring? ({str}) — Check if the string includes the given sub-string. A parameter is required: the string to look for in this string. It will have a boolean.',
|
|
42
|
-
'
|
|
44
|
+
'index_of ({needle} {from}) — Find the 0-based position of the first occurrence of {needle} at or after position {from}. The {needle} parameter is required; {from} is optional and defaults to 0. Does not change the value of the string. It will have the position, or -1 if {needle} is not found; an out-of-range {from} (negative, or past the end of the string) is an error, and it will have false.',
|
|
45
|
+
'format_for_html — Format this string for HTML output. Tabs, spaces and returns are converted to HTML elements. The value of the string is changed. It will have the formatted string.',
|
|
43
46
|
'encode64 — Base64 encode the string. This message changes the value of the string. It will have the encoded string.',
|
|
44
47
|
'decode64 — Decode the string from Base64. This message changes the value of the string. It will have the decoded string.',
|
|
45
48
|
'escape — Escape the string to make it URL safe. This message changes the value of the string. It will have the escaped string.',
|
|
@@ -65,7 +68,7 @@ module Gloo
|
|
|
65
68
|
# Strip whitespace from the beginning and end of the string.
|
|
66
69
|
#
|
|
67
70
|
def msg_trim
|
|
68
|
-
return '' unless value
|
|
71
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
69
72
|
|
|
70
73
|
result = value.strip
|
|
71
74
|
@engine.heap.it.set_to result
|
|
@@ -86,7 +89,7 @@ module Gloo
|
|
|
86
89
|
return result
|
|
87
90
|
else
|
|
88
91
|
# Error
|
|
89
|
-
@engine.
|
|
92
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
90
93
|
@engine.heap.it.set_to false
|
|
91
94
|
return false
|
|
92
95
|
end
|
|
@@ -105,7 +108,7 @@ module Gloo
|
|
|
105
108
|
return result
|
|
106
109
|
else
|
|
107
110
|
# Error
|
|
108
|
-
@engine.
|
|
111
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
109
112
|
@engine.heap.it.set_to false
|
|
110
113
|
return false
|
|
111
114
|
end
|
|
@@ -115,7 +118,7 @@ module Gloo
|
|
|
115
118
|
# Substitute the given string with another string.
|
|
116
119
|
#
|
|
117
120
|
def msg_sub
|
|
118
|
-
return '' unless value
|
|
121
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
119
122
|
if @params&.token_count&.positive?
|
|
120
123
|
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
121
124
|
from = expr.evaluate
|
|
@@ -128,7 +131,7 @@ module Gloo
|
|
|
128
131
|
return result
|
|
129
132
|
else
|
|
130
133
|
# Error
|
|
131
|
-
@engine.
|
|
134
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
132
135
|
@engine.heap.it.set_to false
|
|
133
136
|
return false
|
|
134
137
|
end
|
|
@@ -139,7 +142,7 @@ module Gloo
|
|
|
139
142
|
# Find all occurrences and replace them.
|
|
140
143
|
#
|
|
141
144
|
def msg_gsub
|
|
142
|
-
return '' unless value
|
|
145
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
143
146
|
|
|
144
147
|
if @params&.token_count&.positive?
|
|
145
148
|
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
@@ -153,7 +156,7 @@ module Gloo
|
|
|
153
156
|
return result
|
|
154
157
|
else
|
|
155
158
|
# Error
|
|
156
|
-
@engine.
|
|
159
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
157
160
|
@engine.heap.it.set_to false
|
|
158
161
|
return false
|
|
159
162
|
end
|
|
@@ -165,7 +168,7 @@ module Gloo
|
|
|
165
168
|
# beginning or end of the string. Does not change the string's value.
|
|
166
169
|
#
|
|
167
170
|
def msg_split
|
|
168
|
-
return '' unless value
|
|
171
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
169
172
|
|
|
170
173
|
if @params&.token_count&.positive?
|
|
171
174
|
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
@@ -178,7 +181,7 @@ module Gloo
|
|
|
178
181
|
return result
|
|
179
182
|
else
|
|
180
183
|
# Error
|
|
181
|
-
@engine.
|
|
184
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
182
185
|
@engine.heap.it.set_to false
|
|
183
186
|
return false
|
|
184
187
|
end
|
|
@@ -189,7 +192,7 @@ module Gloo
|
|
|
189
192
|
# Same as split( 0, index ). Does not change the string's value.
|
|
190
193
|
#
|
|
191
194
|
def msg_splitl
|
|
192
|
-
return '' unless value
|
|
195
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
193
196
|
|
|
194
197
|
if @params&.token_count&.positive?
|
|
195
198
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
@@ -200,7 +203,7 @@ module Gloo
|
|
|
200
203
|
return result
|
|
201
204
|
else
|
|
202
205
|
# Error
|
|
203
|
-
@engine.
|
|
206
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
204
207
|
@engine.heap.it.set_to false
|
|
205
208
|
return false
|
|
206
209
|
end
|
|
@@ -211,7 +214,7 @@ module Gloo
|
|
|
211
214
|
# Same as split( index, size ). Does not change the string's value.
|
|
212
215
|
#
|
|
213
216
|
def msg_splitr
|
|
214
|
-
return '' unless value
|
|
217
|
+
return @engine.heap.it.set_to( '' ) unless value
|
|
215
218
|
|
|
216
219
|
if @params&.token_count&.positive?
|
|
217
220
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
@@ -222,7 +225,7 @@ module Gloo
|
|
|
222
225
|
return result
|
|
223
226
|
else
|
|
224
227
|
# Error
|
|
225
|
-
@engine.
|
|
228
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
226
229
|
@engine.heap.it.set_to false
|
|
227
230
|
return false
|
|
228
231
|
end
|
|
@@ -240,10 +243,10 @@ module Gloo
|
|
|
240
243
|
# into 'it'.
|
|
241
244
|
#
|
|
242
245
|
def msg_split_list
|
|
243
|
-
return unless value
|
|
246
|
+
return @engine.heap.it.set_to( 0 ) unless value
|
|
244
247
|
|
|
245
248
|
if @params&.token_count.to_i < 2
|
|
246
|
-
@engine.
|
|
249
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
247
250
|
@engine.heap.it.set_to false
|
|
248
251
|
return false
|
|
249
252
|
end
|
|
@@ -282,9 +285,45 @@ module Gloo
|
|
|
282
285
|
@engine.heap.it.set_to result
|
|
283
286
|
return result
|
|
284
287
|
else
|
|
288
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
289
|
+
@engine.heap.it.set_to false
|
|
290
|
+
return false
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
#
|
|
295
|
+
# Get the 0-based position of the first occurrence of {needle}
|
|
296
|
+
# at or after the optional {from} position (default 0).
|
|
297
|
+
# Puts -1 into 'it' if not found. An out-of-range {from} (negative,
|
|
298
|
+
# or past the end of the string) logs an error and puts false
|
|
299
|
+
# into 'it'. Does not change the string's value.
|
|
300
|
+
#
|
|
301
|
+
def msg_index_of
|
|
302
|
+
unless @params&.token_count&.positive?
|
|
303
|
+
@engine.syntax_err MISSING_PARAM_MSG
|
|
304
|
+
@engine.heap.it.set_to false
|
|
305
|
+
return false
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.first ] )
|
|
309
|
+
needle = expr.evaluate.to_s
|
|
310
|
+
|
|
311
|
+
from = 0
|
|
312
|
+
if @params.token_count > 1
|
|
313
|
+
expr = Gloo::Expr::Expression.new( @engine, [ @params.tokens.last ] )
|
|
314
|
+
from = expr.evaluate.to_i
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
str = value.to_s
|
|
318
|
+
if from.negative? || from > str.length
|
|
319
|
+
@engine.err "Start position #{from} is out of range!"
|
|
285
320
|
@engine.heap.it.set_to false
|
|
286
321
|
return false
|
|
287
322
|
end
|
|
323
|
+
|
|
324
|
+
result = str.index( needle, from ) || -1
|
|
325
|
+
@engine.heap.it.set_to result
|
|
326
|
+
return result
|
|
288
327
|
end
|
|
289
328
|
|
|
290
329
|
#
|
|
@@ -328,26 +367,23 @@ module Gloo
|
|
|
328
367
|
#
|
|
329
368
|
def msg_format_for_html
|
|
330
369
|
text = self.value
|
|
331
|
-
|
|
332
|
-
return out unless text
|
|
370
|
+
return @engine.heap.it.set_to( '' ) unless text
|
|
333
371
|
|
|
334
|
-
#
|
|
372
|
+
# Indentation: each leading space is one , each leading
|
|
373
|
+
# tab four, in whatever order they come.
|
|
374
|
+
out = ""
|
|
335
375
|
text.each_line do |line|
|
|
336
376
|
i = 0
|
|
337
|
-
while line[i]
|
|
377
|
+
while [ ' ', "\t" ].include?( line[ i ] )
|
|
378
|
+
out << ( line[ i ] == ' ' ? ' ' : ' ' )
|
|
338
379
|
i += 1
|
|
339
|
-
out << " "
|
|
340
380
|
end
|
|
341
|
-
|
|
342
|
-
i = 0
|
|
343
|
-
while line[i] == "\t"
|
|
344
|
-
i += 1
|
|
345
|
-
out << " "
|
|
346
|
-
end
|
|
347
|
-
out << line
|
|
381
|
+
out << line[ i..]
|
|
348
382
|
end
|
|
349
|
-
|
|
383
|
+
|
|
350
384
|
self.value = out.gsub( "\n", "<br/>" )
|
|
385
|
+
@engine.heap.it.set_to self.value
|
|
386
|
+
return self.value
|
|
351
387
|
end
|
|
352
388
|
|
|
353
389
|
#
|
|
@@ -515,13 +551,13 @@ module Gloo
|
|
|
515
551
|
#
|
|
516
552
|
# Resolve the target container path for split_list. The path may
|
|
517
553
|
# point directly at a container, or at an alias that points to
|
|
518
|
-
# one. Returns nil (and
|
|
554
|
+
# one. Returns nil (and reports an error, setting 'it' to false)
|
|
519
555
|
# if the path doesn't exist or doesn't resolve to a container.
|
|
520
556
|
#
|
|
521
557
|
def split_list_target( token )
|
|
522
558
|
pn = Gloo::Core::Pn.new( @engine, token )
|
|
523
559
|
unless pn&.exists?
|
|
524
|
-
@engine.
|
|
560
|
+
@engine.err Gloo::Core::NotFound.object( token )
|
|
525
561
|
@engine.heap.it.set_to false
|
|
526
562
|
return nil
|
|
527
563
|
end
|
|
@@ -529,7 +565,7 @@ module Gloo
|
|
|
529
565
|
target = pn.resolve
|
|
530
566
|
target = Gloo::Objs::Alias.resolve_alias( @engine, target )
|
|
531
567
|
unless target&.is_container?
|
|
532
|
-
@engine.
|
|
568
|
+
@engine.err 'Target for split_list must be a container!'
|
|
533
569
|
@engine.heap.it.set_to false
|
|
534
570
|
return nil
|
|
535
571
|
end
|
data/lib/gloo/objs/ctrl/each.rb
CHANGED
|
@@ -133,8 +133,8 @@ module Gloo
|
|
|
133
133
|
'child (alias) — iterate a container\'s children.',
|
|
134
134
|
'word (string) — iterate the words in a string.',
|
|
135
135
|
'line (string) — iterate the lines in a block of text.',
|
|
136
|
-
'file (file) — iterate the files in a folder
|
|
137
|
-
'dir (file) — iterate the directories in a folder.'
|
|
136
|
+
'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.',
|
|
137
|
+
'dir (file) — iterate the directories in a folder. Optional child: recursive (bool) walks the subfolders too.'
|
|
138
138
|
],
|
|
139
139
|
:messages => [
|
|
140
140
|
'run — Run the loop for each item in the collection.'
|
|
@@ -144,7 +144,16 @@ module Gloo
|
|
|
144
144
|
'(string) child naming a property of each child, plus ' \
|
|
145
145
|
'on_group_start / on_group_end (script) children — those ' \
|
|
146
146
|
'scripts run whenever the group_by value changes, letting ' \
|
|
147
|
-
'you aggregate results in groups.'
|
|
147
|
+
'you aggregate results in groups. ' \
|
|
148
|
+
'When iterating files or directories, the in folder may be ' \
|
|
149
|
+
'given with or without a trailing slash, may start with ~ ' \
|
|
150
|
+
'for the home folder, and may include ' \
|
|
151
|
+
'wildcards: in: /notes/**/ walks every subfolder, and ' \
|
|
152
|
+
'in: /projects/*/docs/ looks in each project\'s docs folder. ' \
|
|
153
|
+
'Wildcards combine with ext, recursive and include_dirs. ' \
|
|
154
|
+
'Paths are full paths, sorted, with each folder before ' \
|
|
155
|
+
'what is in it. Hidden files and folders are skipped, and ' \
|
|
156
|
+
'a recursive walk does not go into symlinked folders.',
|
|
148
157
|
:examples => <<~EXAMPLES.strip
|
|
149
158
|
#
|
|
150
159
|
# Show each child in a container.
|
|
@@ -167,6 +176,16 @@ module Gloo
|
|
|
167
176
|
on_load [script] :
|
|
168
177
|
show 'showing children in container' (white)
|
|
169
178
|
tell each_child.for to run
|
|
179
|
+
|
|
180
|
+
#
|
|
181
|
+
# Show every markdown file in a folder tree.
|
|
182
|
+
#
|
|
183
|
+
notes [each] :
|
|
184
|
+
file [file] :
|
|
185
|
+
in [file] : /my/notes/
|
|
186
|
+
ext [string] : md
|
|
187
|
+
recursive [bool] : true
|
|
188
|
+
do [script] : show ^.file
|
|
170
189
|
EXAMPLES
|
|
171
190
|
}
|
|
172
191
|
end
|
|
@@ -9,6 +9,7 @@ module Gloo
|
|
|
9
9
|
class EachDir
|
|
10
10
|
|
|
11
11
|
DIR = 'dir'.freeze
|
|
12
|
+
RECURSIVE = 'recursive'.freeze
|
|
12
13
|
|
|
13
14
|
# ---------------------------------------------------------------------
|
|
14
15
|
# Create Iterator
|
|
@@ -42,21 +43,33 @@ module Gloo
|
|
|
42
43
|
# Run for each directory.
|
|
43
44
|
#
|
|
44
45
|
def run
|
|
45
|
-
folder = @iterator_obj.in_value
|
|
46
|
+
folder = EachFile.expand_home( @iterator_obj.in_value )
|
|
46
47
|
return unless folder
|
|
47
48
|
|
|
48
49
|
unless Dir.exist?( folder )
|
|
49
|
-
|
|
50
|
+
# A path with a wildcard is a pattern, not a folder.
|
|
51
|
+
unless EachFile.wildcard?( folder )
|
|
52
|
+
@engine.err Gloo::Core::NotFound.folder( folder )
|
|
53
|
+
return
|
|
54
|
+
end
|
|
50
55
|
end
|
|
51
56
|
|
|
52
|
-
Dir.glob(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
Dir.glob( EachFile.pattern( folder, recursive? ) ).each do |f|
|
|
58
|
+
next unless File.directory?( f )
|
|
59
|
+
|
|
60
|
+
set_dir f
|
|
61
|
+
@iterator_obj.run_do
|
|
57
62
|
end
|
|
58
63
|
end
|
|
59
64
|
|
|
65
|
+
#
|
|
66
|
+
# Walk the subfolders too?
|
|
67
|
+
#
|
|
68
|
+
def recursive?
|
|
69
|
+
value = @iterator_obj.find_child_value RECURSIVE
|
|
70
|
+
return Gloo::Objs::Boolean.coerse_to_bool( value )
|
|
71
|
+
end
|
|
72
|
+
|
|
60
73
|
#
|
|
61
74
|
# Set the value of the dir.
|
|
62
75
|
#
|
|
@@ -10,7 +10,10 @@ module Gloo
|
|
|
10
10
|
|
|
11
11
|
FILE = 'file'.freeze
|
|
12
12
|
EXT = 'ext'.freeze
|
|
13
|
+
RECURSIVE = 'recursive'.freeze
|
|
14
|
+
INCLUDE_DIRS = 'include_dirs'.freeze
|
|
13
15
|
WILD = '*'.freeze
|
|
16
|
+
RECURSE = '**'.freeze
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
# ---------------------------------------------------------------------
|
|
@@ -45,7 +48,7 @@ module Gloo
|
|
|
45
48
|
# Run for each file.
|
|
46
49
|
#
|
|
47
50
|
def run
|
|
48
|
-
folder = @iterator_obj.in_value
|
|
51
|
+
folder = EachFile.expand_home( @iterator_obj.in_value )
|
|
49
52
|
return unless folder
|
|
50
53
|
|
|
51
54
|
unless Dir.exist?( folder )
|
|
@@ -53,20 +56,86 @@ module Gloo
|
|
|
53
56
|
@engine.log.info "Folder does not exist: #{folder}"
|
|
54
57
|
end
|
|
55
58
|
|
|
56
|
-
Dir.glob(
|
|
59
|
+
Dir.glob( EachFile.pattern( folder, recursive? ) ).each do |f|
|
|
60
|
+
next unless include?( f )
|
|
61
|
+
|
|
57
62
|
set_file f
|
|
58
63
|
@iterator_obj.run_do
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return
|
|
67
|
+
#
|
|
68
|
+
# Expand a leading ~ to the home folder.
|
|
69
|
+
# Other paths are left as they are, so relative paths stay relative.
|
|
70
|
+
#
|
|
71
|
+
def self.expand_home( folder )
|
|
72
|
+
return folder unless folder.is_a?( ::String ) && folder.start_with?( '~' )
|
|
73
|
+
|
|
74
|
+
return File.expand_path( folder )
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#
|
|
78
|
+
# Get the glob pattern for everything in the folder, and
|
|
79
|
+
# optionally everything in its subfolders.
|
|
80
|
+
# The folder may itself include wildcards.
|
|
81
|
+
#
|
|
82
|
+
def self.pattern( folder, recursive )
|
|
83
|
+
return File.join( folder, RECURSE, WILD ) if recursive
|
|
84
|
+
|
|
85
|
+
return File.join( folder, WILD )
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
#
|
|
89
|
+
# Does the folder path include glob wildcards?
|
|
90
|
+
#
|
|
91
|
+
def self.wildcard?( folder )
|
|
92
|
+
return folder.to_s.match?( /[*?\[{]/ )
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
#
|
|
96
|
+
# Should the loop run for this path?
|
|
97
|
+
# Folders only when include_dirs is set; files only when they
|
|
98
|
+
# match the extension, if there is one.
|
|
99
|
+
#
|
|
100
|
+
def include?( f )
|
|
101
|
+
return include_dirs? if File.directory?( f )
|
|
102
|
+
return false unless File.file?( f )
|
|
103
|
+
|
|
104
|
+
return ext_match?( f )
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
#
|
|
108
|
+
# Does the file have the extension we're looking for?
|
|
109
|
+
# The match ignores case, so md matches .MD on every platform.
|
|
110
|
+
#
|
|
111
|
+
def ext_match?( f )
|
|
112
|
+
ext = @iterator_obj.find_child_value EXT
|
|
113
|
+
return true if ext.nil? || ext.to_s.strip.empty?
|
|
68
114
|
|
|
69
|
-
|
|
115
|
+
ext = ext.to_s.strip.delete_prefix( '.' )
|
|
116
|
+
return File.extname( f ).casecmp?( ".#{ext}" )
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
#
|
|
120
|
+
# Walk the subfolders too?
|
|
121
|
+
#
|
|
122
|
+
def recursive?
|
|
123
|
+
return child_true?( RECURSIVE )
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
#
|
|
127
|
+
# List folders as well as files?
|
|
128
|
+
#
|
|
129
|
+
def include_dirs?
|
|
130
|
+
return child_true?( INCLUDE_DIRS )
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
#
|
|
134
|
+
# Is the named child present and true?
|
|
135
|
+
#
|
|
136
|
+
def child_true?( name )
|
|
137
|
+
value = @iterator_obj.find_child_value name
|
|
138
|
+
return Gloo::Objs::Boolean.coerse_to_bool( value )
|
|
70
139
|
end
|
|
71
140
|
|
|
72
141
|
#
|
|
@@ -164,10 +164,10 @@ module Gloo
|
|
|
164
164
|
#
|
|
165
165
|
# Invoke the function, run the script and return the result.
|
|
166
166
|
#
|
|
167
|
-
# If on_invoke itself hits an error,
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
# state (@engine.heap.error)
|
|
167
|
+
# If on_invoke itself hits an error, there is no reliable
|
|
168
|
+
# result, so it is set to false (like any other command that
|
|
169
|
+
# would set it and fails), and the engine is left in an error
|
|
170
|
+
# state (@engine.heap.error).
|
|
171
171
|
#
|
|
172
172
|
def invoke args
|
|
173
173
|
@engine.log.debug "Invoking function: #{name}"
|
|
@@ -179,6 +179,7 @@ module Gloo
|
|
|
179
179
|
failed = @engine.heap.error.error_count.positive?
|
|
180
180
|
|
|
181
181
|
if failed
|
|
182
|
+
@engine.heap.it.set_to false
|
|
182
183
|
run_after_invoke
|
|
183
184
|
return nil
|
|
184
185
|
end
|
|
@@ -230,7 +231,7 @@ module Gloo
|
|
|
230
231
|
'result (string) — The result of the function to return to the caller.'
|
|
231
232
|
],
|
|
232
233
|
:messages => [
|
|
233
|
-
'invoke — Invoke the function, set it and return the result. The function result is put into it as well as being returned to the caller.'
|
|
234
|
+
'invoke — Invoke the function, set it and return the result. The function result is put into it as well as being returned to the caller. If on_invoke hits an error, there is no result: it is false, and after_invoke still runs.'
|
|
234
235
|
],
|
|
235
236
|
:examples => <<~EXAMPLES.strip
|
|
236
237
|
#
|
data/lib/gloo/objs/dt/date.rb
CHANGED
|
@@ -73,14 +73,17 @@ module Gloo
|
|
|
73
73
|
# Add the given modifier to the date.
|
|
74
74
|
#
|
|
75
75
|
def msg_add
|
|
76
|
-
|
|
76
|
+
data = nil
|
|
77
77
|
if @params&.token_count&.positive?
|
|
78
78
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
79
79
|
data = expr.evaluate
|
|
80
|
-
modifier = data
|
|
81
80
|
end
|
|
81
|
+
modifier = DtTools.amount( @engine, data )
|
|
82
|
+
return false unless modifier
|
|
82
83
|
|
|
83
|
-
dt =
|
|
84
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
85
|
+
return false unless dt
|
|
86
|
+
|
|
84
87
|
new_value = DtTools.add( dt, modifier )
|
|
85
88
|
self.set_value( new_value )
|
|
86
89
|
@engine.heap.it.set_to self.value
|
|
@@ -90,14 +93,17 @@ module Gloo
|
|
|
90
93
|
# Subtract the given modifier from the date.
|
|
91
94
|
#
|
|
92
95
|
def msg_sub
|
|
93
|
-
|
|
96
|
+
data = nil
|
|
94
97
|
if @params&.token_count&.positive?
|
|
95
98
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
96
99
|
data = expr.evaluate
|
|
97
|
-
modifier = data
|
|
98
100
|
end
|
|
101
|
+
modifier = DtTools.amount( @engine, data )
|
|
102
|
+
return false unless modifier
|
|
103
|
+
|
|
104
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
105
|
+
return false unless dt
|
|
99
106
|
|
|
100
|
-
dt = Chronic.parse( self.value )
|
|
101
107
|
self.set_value( DtTools.sub( dt, modifier ) )
|
|
102
108
|
@engine.heap.it.set_to self.value
|
|
103
109
|
end
|
|
@@ -106,7 +112,9 @@ module Gloo
|
|
|
106
112
|
# Get the month.
|
|
107
113
|
#
|
|
108
114
|
def msg_mm
|
|
109
|
-
dt =
|
|
115
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
116
|
+
return false unless dt
|
|
117
|
+
|
|
110
118
|
@engine.heap.it.set_to "#{dt.month}".rjust(2, '0')
|
|
111
119
|
end
|
|
112
120
|
|
|
@@ -114,7 +122,9 @@ module Gloo
|
|
|
114
122
|
# Get the day.
|
|
115
123
|
#
|
|
116
124
|
def msg_dd
|
|
117
|
-
dt =
|
|
125
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
126
|
+
return false unless dt
|
|
127
|
+
|
|
118
128
|
@engine.heap.it.set_to "#{dt.day}".rjust(2, '0')
|
|
119
129
|
end
|
|
120
130
|
|
|
@@ -122,7 +132,9 @@ module Gloo
|
|
|
122
132
|
# Get the year.
|
|
123
133
|
#
|
|
124
134
|
def msg_yyyy
|
|
125
|
-
dt =
|
|
135
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
136
|
+
return false unless dt
|
|
137
|
+
|
|
126
138
|
@engine.heap.it.set_to dt.year.to_s
|
|
127
139
|
end
|
|
128
140
|
|
|
@@ -130,7 +142,9 @@ module Gloo
|
|
|
130
142
|
# Get the year (2 digit).
|
|
131
143
|
#
|
|
132
144
|
def msg_yy
|
|
133
|
-
dt =
|
|
145
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
146
|
+
return false unless dt
|
|
147
|
+
|
|
134
148
|
@engine.heap.it.set_to dt.year.to_s[-2..-1]
|
|
135
149
|
end
|
|
136
150
|
|
|
@@ -146,7 +160,9 @@ module Gloo
|
|
|
146
160
|
data = expr.evaluate
|
|
147
161
|
format = data
|
|
148
162
|
end
|
|
149
|
-
dt =
|
|
163
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
164
|
+
return false unless dt
|
|
165
|
+
|
|
150
166
|
@engine.heap.it.set_to dt.strftime( format )
|
|
151
167
|
end
|
|
152
168
|
|
|
@@ -175,6 +191,7 @@ module Gloo
|
|
|
175
191
|
'yyyy — Get the 4 digit year portion of the date. Put the 4 digit year into it.',
|
|
176
192
|
'format ({fmt}) — Format the date using a strftime-style format string. Default if none given: %Y-%m-%d. It will have the formatted string.'
|
|
177
193
|
],
|
|
194
|
+
:notes => 'A value that isn\'t a date (eg. \'2026-02-31\') is left with no value, with a warning: "\'2026-02-31\' is not a date; using no value."',
|
|
178
195
|
:examples => <<~EXAMPLES.strip
|
|
179
196
|
#
|
|
180
197
|
# Show the current date.
|