gloo 6.7.1 → 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 +24 -0
- data/lib/gloo/app/engine.rb +65 -9
- 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/dictionary.rb +30 -21
- 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/callback.rb +2 -2
- 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 +6 -6
|
@@ -69,14 +69,17 @@ module Gloo
|
|
|
69
69
|
# Add the given modifier to the date.
|
|
70
70
|
#
|
|
71
71
|
def msg_add
|
|
72
|
-
|
|
72
|
+
data = nil
|
|
73
73
|
if @params&.token_count&.positive?
|
|
74
74
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
75
75
|
data = expr.evaluate
|
|
76
|
-
modifier = data
|
|
77
76
|
end
|
|
77
|
+
modifier = DtTools.amount( @engine, data )
|
|
78
|
+
return false unless modifier
|
|
78
79
|
|
|
79
|
-
dt =
|
|
80
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
81
|
+
return false unless dt
|
|
82
|
+
|
|
80
83
|
new_value = DtTools.add( dt, modifier )
|
|
81
84
|
self.set_value( new_value )
|
|
82
85
|
@engine.heap.it.set_to self.value
|
|
@@ -86,14 +89,17 @@ module Gloo
|
|
|
86
89
|
# Subtract the given modifier from the date.
|
|
87
90
|
#
|
|
88
91
|
def msg_sub
|
|
89
|
-
|
|
92
|
+
data = nil
|
|
90
93
|
if @params&.token_count&.positive?
|
|
91
94
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
92
95
|
data = expr.evaluate
|
|
93
|
-
modifier = data
|
|
94
96
|
end
|
|
97
|
+
modifier = DtTools.amount( @engine, data )
|
|
98
|
+
return false unless modifier
|
|
99
|
+
|
|
100
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
101
|
+
return false unless dt
|
|
95
102
|
|
|
96
|
-
dt = Chronic.parse( self.value )
|
|
97
103
|
self.set_value( DtTools.sub( dt, modifier ) )
|
|
98
104
|
@engine.heap.it.set_to self.value
|
|
99
105
|
end
|
|
@@ -102,7 +108,10 @@ module Gloo
|
|
|
102
108
|
# Set the value to the beginning of the month.
|
|
103
109
|
#
|
|
104
110
|
def msg_begin_month
|
|
105
|
-
dt = self.value.
|
|
111
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
112
|
+
return false unless dt
|
|
113
|
+
|
|
114
|
+
dt = dt.to_time.beginning_of_month
|
|
106
115
|
self.set_value dt
|
|
107
116
|
@engine.heap.it.set_to dt
|
|
108
117
|
end
|
|
@@ -111,7 +120,10 @@ module Gloo
|
|
|
111
120
|
# Set the value to the end of the month.
|
|
112
121
|
#
|
|
113
122
|
def msg_end_month
|
|
114
|
-
dt = self.value.
|
|
123
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
124
|
+
return false unless dt
|
|
125
|
+
|
|
126
|
+
dt = dt.to_time.end_of_month
|
|
115
127
|
self.set_value dt
|
|
116
128
|
@engine.heap.it.set_to dt
|
|
117
129
|
end
|
|
@@ -120,7 +132,10 @@ module Gloo
|
|
|
120
132
|
# Set the value to the beginning of the year.
|
|
121
133
|
#
|
|
122
134
|
def msg_begin_year
|
|
123
|
-
dt = self.value.
|
|
135
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
136
|
+
return false unless dt
|
|
137
|
+
|
|
138
|
+
dt = dt.to_time.beginning_of_year
|
|
124
139
|
self.set_value dt
|
|
125
140
|
@engine.heap.it.set_to dt
|
|
126
141
|
end
|
|
@@ -129,7 +144,10 @@ module Gloo
|
|
|
129
144
|
# Set the value to the end of the year.
|
|
130
145
|
#
|
|
131
146
|
def msg_end_year
|
|
132
|
-
dt = self.value.
|
|
147
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
148
|
+
return false unless dt
|
|
149
|
+
|
|
150
|
+
dt = dt.to_time.end_of_year
|
|
133
151
|
self.set_value dt
|
|
134
152
|
@engine.heap.it.set_to dt
|
|
135
153
|
end
|
|
@@ -138,7 +156,10 @@ module Gloo
|
|
|
138
156
|
# Set the value to the beginning of the week.
|
|
139
157
|
#
|
|
140
158
|
def msg_begin_week
|
|
141
|
-
dt = self.value.
|
|
159
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
160
|
+
return false unless dt
|
|
161
|
+
|
|
162
|
+
dt = dt.to_time.beginning_of_week( start_day = :sunday )
|
|
142
163
|
self.set_value dt
|
|
143
164
|
@engine.heap.it.set_to dt
|
|
144
165
|
end
|
|
@@ -147,7 +168,10 @@ module Gloo
|
|
|
147
168
|
# Set the value to the end of the week.
|
|
148
169
|
#
|
|
149
170
|
def msg_end_week
|
|
150
|
-
dt = self.value.
|
|
171
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
172
|
+
return false unless dt
|
|
173
|
+
|
|
174
|
+
dt = dt.to_time.end_of_week( start_day = :sunday )
|
|
151
175
|
self.set_value dt
|
|
152
176
|
@engine.heap.it.set_to dt
|
|
153
177
|
end
|
|
@@ -156,7 +180,10 @@ module Gloo
|
|
|
156
180
|
# Set the value to the beginning of the day.
|
|
157
181
|
#
|
|
158
182
|
def msg_begin_day
|
|
159
|
-
dt = self.value.
|
|
183
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
184
|
+
return false unless dt
|
|
185
|
+
|
|
186
|
+
dt = dt.to_time.beginning_of_day
|
|
160
187
|
self.set_value dt
|
|
161
188
|
@engine.heap.it.set_to dt
|
|
162
189
|
end
|
|
@@ -165,7 +192,10 @@ module Gloo
|
|
|
165
192
|
# Set the value to the end of the day.
|
|
166
193
|
#
|
|
167
194
|
def msg_end_day
|
|
168
|
-
dt = self.value.
|
|
195
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
196
|
+
return false unless dt
|
|
197
|
+
|
|
198
|
+
dt = dt.to_time.end_of_day
|
|
169
199
|
self.set_value dt
|
|
170
200
|
@engine.heap.it.set_to dt
|
|
171
201
|
end
|
|
@@ -175,7 +205,10 @@ module Gloo
|
|
|
175
205
|
# Tell the datetime to check if it is today.
|
|
176
206
|
#
|
|
177
207
|
def msg_is_today
|
|
178
|
-
|
|
208
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
209
|
+
return false unless dt
|
|
210
|
+
|
|
211
|
+
today = DtTools.is_today?( dt )
|
|
179
212
|
@engine.heap.it.set_to today
|
|
180
213
|
return today
|
|
181
214
|
end
|
|
@@ -184,7 +217,10 @@ module Gloo
|
|
|
184
217
|
# Tell the datetime to check if it is in the future.
|
|
185
218
|
#
|
|
186
219
|
def msg_is_future
|
|
187
|
-
|
|
220
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
221
|
+
return false unless dt
|
|
222
|
+
|
|
223
|
+
today = DtTools.is_future?( dt )
|
|
188
224
|
@engine.heap.it.set_to today
|
|
189
225
|
return today
|
|
190
226
|
end
|
|
@@ -193,7 +229,10 @@ module Gloo
|
|
|
193
229
|
# Tell the datetime to check if it is in the past.
|
|
194
230
|
#
|
|
195
231
|
def msg_is_past
|
|
196
|
-
|
|
232
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
233
|
+
return false unless dt
|
|
234
|
+
|
|
235
|
+
today = DtTools.is_past?( dt )
|
|
197
236
|
@engine.heap.it.set_to today
|
|
198
237
|
return today
|
|
199
238
|
end
|
|
@@ -202,7 +241,10 @@ module Gloo
|
|
|
202
241
|
# Tell the datetime to check if it is yesterday.
|
|
203
242
|
#
|
|
204
243
|
def msg_is_yesterday
|
|
205
|
-
|
|
244
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
245
|
+
return false unless dt
|
|
246
|
+
|
|
247
|
+
today = DtTools.is_yesterday?( dt )
|
|
206
248
|
@engine.heap.it.set_to today
|
|
207
249
|
return today
|
|
208
250
|
end
|
|
@@ -211,7 +253,10 @@ module Gloo
|
|
|
211
253
|
# Tell the datetime to check if it is tomorrow.
|
|
212
254
|
#
|
|
213
255
|
def msg_is_tomorrow
|
|
214
|
-
|
|
256
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
257
|
+
return false unless dt
|
|
258
|
+
|
|
259
|
+
today = DtTools.is_tomorrow?( dt )
|
|
215
260
|
@engine.heap.it.set_to today
|
|
216
261
|
return today
|
|
217
262
|
end
|
|
@@ -220,7 +265,10 @@ module Gloo
|
|
|
220
265
|
# Tell the datetime to check if it is this week.
|
|
221
266
|
#
|
|
222
267
|
def msg_is_this_week
|
|
223
|
-
|
|
268
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
269
|
+
return false unless dt
|
|
270
|
+
|
|
271
|
+
today = DtTools.is_this_week?( dt )
|
|
224
272
|
@engine.heap.it.set_to today
|
|
225
273
|
return today
|
|
226
274
|
end
|
|
@@ -245,7 +293,9 @@ module Gloo
|
|
|
245
293
|
data = expr.evaluate
|
|
246
294
|
format = data
|
|
247
295
|
end
|
|
248
|
-
dt =
|
|
296
|
+
dt = DtTools.parse_value( @engine, self.value, self.class.typename )
|
|
297
|
+
return false unless dt
|
|
298
|
+
|
|
249
299
|
@engine.heap.it.set_to dt.strftime( format )
|
|
250
300
|
end
|
|
251
301
|
|
|
@@ -284,6 +334,7 @@ module Gloo
|
|
|
284
334
|
'end_year — Set the datetime to the end of the year.',
|
|
285
335
|
'format ({fmt}) — Format the date and time using a strftime-style format string. Default if none given: %Y-%m-%d %H:%M:%S. It will have the formatted string.'
|
|
286
336
|
],
|
|
337
|
+
:notes => 'A value that isn\'t a date and time is left with no value, with a warning. A clock time out of range (eg. \'25:99\') is used as a best guess, with a warning that shows the value used.',
|
|
287
338
|
:examples => <<~EXAMPLES.strip
|
|
288
339
|
#
|
|
289
340
|
# Show the current date and time.
|
|
@@ -22,6 +22,50 @@ class DtTools
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
UNITS = %w[second minute hour day week fortnight month year].freeze
|
|
26
|
+
|
|
27
|
+
#
|
|
28
|
+
# Get a date/time object's value as a Time, for a message to work
|
|
29
|
+
# with. If it isn't a date or time (Chronic can't read it), report
|
|
30
|
+
# that, put false into it, and return nil.
|
|
31
|
+
#
|
|
32
|
+
def self.parse_value( engine, value, kind )
|
|
33
|
+
unless value.blank?
|
|
34
|
+
dt = is_dt_type?( value ) ? value : Chronic.parse( value.to_s )
|
|
35
|
+
return dt if dt
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
msg = value.blank? ? "There is no #{kind} to work with!" : "'#{value}' is not a #{kind}."
|
|
39
|
+
engine.err msg
|
|
40
|
+
engine.heap.it.set_to false
|
|
41
|
+
return nil
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Get the amount for add or sub, in the form "1 day" or "3 months".
|
|
46
|
+
# No amount is 1 of the default unit. A number with no unit is that
|
|
47
|
+
# many of the default unit (a best guess, with a warning). Anything
|
|
48
|
+
# else that isn't a number and a unit is reported; put false into it
|
|
49
|
+
# and return nil.
|
|
50
|
+
#
|
|
51
|
+
def self.amount( engine, data, default_unit = 'day' )
|
|
52
|
+
return "1 #{default_unit}" if data.nil?
|
|
53
|
+
|
|
54
|
+
text = data.to_s.strip
|
|
55
|
+
if text.match?( /\A-?\d+\z/ )
|
|
56
|
+
unit = text == '1' ? default_unit : "#{default_unit}s"
|
|
57
|
+
engine.warn "'#{text}' has no unit; using #{text} #{unit}."
|
|
58
|
+
return "#{text} #{default_unit}"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
count, unit = text.split( ' ' )
|
|
62
|
+
return text if count&.match?( /\A-?\d+\z/ ) && UNITS.include?( unit.to_s.downcase.chomp( 's' ) )
|
|
63
|
+
|
|
64
|
+
engine.err "'#{text}' is not an amount like '1 day' or '3 months'."
|
|
65
|
+
engine.heap.it.set_to false
|
|
66
|
+
return nil
|
|
67
|
+
end
|
|
68
|
+
|
|
25
69
|
# ---------------------------------------------------------------------
|
|
26
70
|
# Date Math
|
|
27
71
|
# ---------------------------------------------------------------------
|
|
@@ -35,7 +79,7 @@ class DtTools
|
|
|
35
79
|
amount, unit = modifier.split(' ')
|
|
36
80
|
|
|
37
81
|
# converts "1 day" to 1.day
|
|
38
|
-
duration = amount.to_i.send( unit )
|
|
82
|
+
duration = amount.to_i.send( unit.downcase )
|
|
39
83
|
return date + duration
|
|
40
84
|
end
|
|
41
85
|
|
|
@@ -48,7 +92,7 @@ class DtTools
|
|
|
48
92
|
amount, unit = modifier.split(' ')
|
|
49
93
|
|
|
50
94
|
# converts "1 day" to 1.day
|
|
51
|
-
duration = amount.to_i.send( unit )
|
|
95
|
+
duration = amount.to_i.send( unit.downcase )
|
|
52
96
|
return dt - duration
|
|
53
97
|
end
|
|
54
98
|
|
data/lib/gloo/objs/dt/time.rb
CHANGED
|
@@ -73,14 +73,17 @@ module Gloo
|
|
|
73
73
|
# Add the given modifier to the time.
|
|
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, 'hour' )
|
|
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 time.
|
|
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, 'hour' )
|
|
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 hour.
|
|
107
113
|
#
|
|
108
114
|
def msg_hh
|
|
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.hour}".rjust(2, '0')
|
|
111
119
|
end
|
|
112
120
|
|
|
@@ -114,7 +122,9 @@ module Gloo
|
|
|
114
122
|
# Get the minute.
|
|
115
123
|
#
|
|
116
124
|
def msg_mm
|
|
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.min}".rjust(2, '0')
|
|
119
129
|
end
|
|
120
130
|
|
|
@@ -122,7 +132,9 @@ module Gloo
|
|
|
122
132
|
# Get the second.
|
|
123
133
|
#
|
|
124
134
|
def msg_ss
|
|
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.sec}".rjust(2, '0')
|
|
127
139
|
end
|
|
128
140
|
|
|
@@ -130,7 +142,9 @@ module Gloo
|
|
|
130
142
|
# Get the AM/PM.
|
|
131
143
|
#
|
|
132
144
|
def msg_am
|
|
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.strftime( '%p' )
|
|
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
|
'am — Get the am/pm portion of the time. Put "am" or "pm" into it.',
|
|
176
192
|
'format ({fmt}) — Format the time using a strftime-style format string. Default if none given: %H:%M:%S. It will have the formatted string.'
|
|
177
193
|
],
|
|
194
|
+
:notes => 'A value that isn\'t a time is left with no value, with a warning. A clock time out of range (eg. \'25:99\') is used as a best guess, with a warning that shows the time used.',
|
|
178
195
|
:examples => <<~EXAMPLES.strip
|
|
179
196
|
#
|
|
180
197
|
# Show the current time.
|
|
@@ -14,6 +14,10 @@ module Gloo
|
|
|
14
14
|
KEYWORD_SHORT = 'crypt'.freeze
|
|
15
15
|
|
|
16
16
|
CIPHER_TYPE = 'AES-256-CBC'.freeze
|
|
17
|
+
|
|
18
|
+
# A key or vector of the wrong size, or data encrypted with a
|
|
19
|
+
# different key.
|
|
20
|
+
CIPHER_ERRORS = [ ArgumentError, OpenSSL::Cipher::CipherError ].freeze
|
|
17
21
|
KEY = 'key'.freeze
|
|
18
22
|
INIT_VECTOR = 'init_vector'.freeze
|
|
19
23
|
DATA = 'data'.freeze
|
|
@@ -81,6 +85,7 @@ module Gloo
|
|
|
81
85
|
# Update the data value of the object.
|
|
82
86
|
#
|
|
83
87
|
def update_data( new_val )
|
|
88
|
+
@engine.heap.it.set_to new_val
|
|
84
89
|
o = find_child_resolve_alias DATA
|
|
85
90
|
return unless o
|
|
86
91
|
|
|
@@ -143,14 +148,22 @@ module Gloo
|
|
|
143
148
|
# Decrypt the encrypted child object.
|
|
144
149
|
#
|
|
145
150
|
def msg_decrypt
|
|
146
|
-
|
|
151
|
+
result = nil
|
|
152
|
+
return false unless attempt( "decrypt #{pn}", *CIPHER_ERRORS ) do
|
|
153
|
+
result = Cipher.decrypt( data, key, init_vector )
|
|
154
|
+
end
|
|
155
|
+
update_data result
|
|
147
156
|
end
|
|
148
157
|
|
|
149
158
|
#
|
|
150
159
|
# Encrypt the decrypted child object.
|
|
151
160
|
#
|
|
152
161
|
def msg_encrypt
|
|
153
|
-
|
|
162
|
+
result = nil
|
|
163
|
+
return false unless attempt( "encrypt #{pn}", *CIPHER_ERRORS ) do
|
|
164
|
+
result = Cipher.encrypt( data, key, init_vector )
|
|
165
|
+
end
|
|
166
|
+
update_data result
|
|
154
167
|
end
|
|
155
168
|
|
|
156
169
|
# ---------------------------------------------------------------------
|
|
@@ -204,8 +217,8 @@ module Gloo
|
|
|
204
217
|
],
|
|
205
218
|
:messages => [
|
|
206
219
|
'generate_keys — Generate an encryption key and initialization vector. The keys are base64 encoded.',
|
|
207
|
-
'encrypt — Encrypt the data.',
|
|
208
|
-
'decrypt — Decrypt the data.'
|
|
220
|
+
'encrypt — Encrypt the data. The data child is changed. It will have the encrypted data.',
|
|
221
|
+
'decrypt — Decrypt the data. The data child is changed. It will have the decrypted data.'
|
|
209
222
|
],
|
|
210
223
|
:examples => <<~EXAMPLES.strip
|
|
211
224
|
#
|
|
@@ -79,6 +79,7 @@ module Gloo
|
|
|
79
79
|
# Update the hashed password value.
|
|
80
80
|
#
|
|
81
81
|
def update_hash( new_hash )
|
|
82
|
+
@engine.heap.it.set_to new_hash
|
|
82
83
|
o = find_child_resolve_alias HASH
|
|
83
84
|
return unless o
|
|
84
85
|
|
|
@@ -154,7 +155,10 @@ module Gloo
|
|
|
154
155
|
# Uses the salt and the hash to check the password.
|
|
155
156
|
#
|
|
156
157
|
def msg_check
|
|
157
|
-
hashed_pwd =
|
|
158
|
+
hashed_pwd = nil
|
|
159
|
+
return false unless attempt( "check the password against the hash in #{pn}", BCrypt::Errors::InvalidHash ) do
|
|
160
|
+
hashed_pwd = BCrypt::Password.new( hash )
|
|
161
|
+
end
|
|
158
162
|
result = ( hashed_pwd == salt_pwd )
|
|
159
163
|
@engine.heap.it.set_to result
|
|
160
164
|
end
|
|
@@ -178,7 +182,7 @@ module Gloo
|
|
|
178
182
|
],
|
|
179
183
|
:messages => [
|
|
180
184
|
'generate ({len}) — Generate a new password, put in the password child object. The {len} parameter is optional; the length is 7 if not specified.',
|
|
181
|
-
'hash — Hash the password in the child object. Put it in the hash child object.',
|
|
185
|
+
'hash — Hash the password in the child object. Put it in the hash child object. It will have the hash.',
|
|
182
186
|
'check — Check the password in the child object. Is it the same as the hash?'
|
|
183
187
|
],
|
|
184
188
|
:examples => <<~EXAMPLES.strip
|
data/lib/gloo/objs/system/erb.rb
CHANGED
|
@@ -42,6 +42,7 @@ module Gloo
|
|
|
42
42
|
# Set the result of the ERB template conversion.
|
|
43
43
|
#
|
|
44
44
|
def set_result( data )
|
|
45
|
+
@engine.heap.it.set_to data
|
|
45
46
|
r = find_child_resolve_alias RESULT
|
|
46
47
|
return unless r
|
|
47
48
|
|
|
@@ -106,8 +107,13 @@ module Gloo
|
|
|
106
107
|
tmpl = template_value
|
|
107
108
|
return unless tmpl
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
# The template is Ruby code the script wrote, so anything it
|
|
111
|
+
# raises (including a syntax error) is the script's problem.
|
|
112
|
+
result = nil
|
|
113
|
+
return false unless attempt( "render the template in #{pn}", StandardError, ScriptError ) do
|
|
114
|
+
result = ERB.new( tmpl ).result_with_hash( param_hash )
|
|
115
|
+
end
|
|
116
|
+
set_result result
|
|
111
117
|
end
|
|
112
118
|
|
|
113
119
|
# ---------------------------------------------------------------------
|
|
@@ -130,7 +136,7 @@ module Gloo
|
|
|
130
136
|
'result (text) — The result of the template rendering.'
|
|
131
137
|
],
|
|
132
138
|
:messages => [
|
|
133
|
-
'run — Render the result based on the template and the parameter values.'
|
|
139
|
+
'run — Render the result based on the template and the parameter values. It will have the rendered result.'
|
|
134
140
|
],
|
|
135
141
|
:examples => <<~EXAMPLES.strip
|
|
136
142
|
e [can] :
|
|
@@ -12,7 +12,6 @@ module Gloo
|
|
|
12
12
|
KEYWORD_SHORT = 'dir'.freeze
|
|
13
13
|
|
|
14
14
|
FILE_NAME_ERR = 'file and path name expected'.freeze
|
|
15
|
-
FILE_MISSING_ERR = 'file not found'.freeze
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
#
|
|
@@ -59,7 +58,7 @@ module Gloo
|
|
|
59
58
|
# Show the contents of the file, paginated.
|
|
60
59
|
#
|
|
61
60
|
def msg_page
|
|
62
|
-
return unless
|
|
61
|
+
return unless check_is_file?
|
|
63
62
|
|
|
64
63
|
system "less #{value}"
|
|
65
64
|
end
|
|
@@ -68,25 +67,32 @@ module Gloo
|
|
|
68
67
|
# Show the contents of the file.
|
|
69
68
|
#
|
|
70
69
|
def msg_show
|
|
71
|
-
return unless
|
|
70
|
+
return unless check_is_file?
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
data = file_op( 'read' ) { File.read( value ) }
|
|
73
|
+
puts data if data
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
#
|
|
77
77
|
# Read the contents of the file into the object.
|
|
78
78
|
#
|
|
79
79
|
def msg_read
|
|
80
|
-
return unless check_file_exists?
|
|
80
|
+
return @engine.heap.it.set_to( false ) unless check_file_exists?
|
|
81
|
+
|
|
82
|
+
data = file_op( 'read' ) { File.read( value ) }
|
|
83
|
+
return @engine.heap.it.set_to( false ) unless data
|
|
81
84
|
|
|
82
|
-
data = File.read( value )
|
|
83
85
|
if @params&.token_count&.positive?
|
|
84
86
|
pn = Gloo::Core::Pn.new( @engine, @params.first )
|
|
85
87
|
o = pn.resolve
|
|
88
|
+
unless o
|
|
89
|
+
@engine.err Gloo::Core::NotFound.object( @params.first )
|
|
90
|
+
return @engine.heap.it.set_to( false )
|
|
91
|
+
end
|
|
92
|
+
|
|
86
93
|
o.set_value data
|
|
87
|
-
else
|
|
88
|
-
@engine.heap.it.set_to data
|
|
89
94
|
end
|
|
95
|
+
@engine.heap.it.set_to data
|
|
90
96
|
end
|
|
91
97
|
|
|
92
98
|
#
|
|
@@ -101,9 +107,11 @@ module Gloo
|
|
|
101
107
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
102
108
|
data = expr.evaluate
|
|
103
109
|
end
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
file_op( 'append to' ) do
|
|
111
|
+
existing = File.exist?( value ) ? File.read( value ) : ''
|
|
112
|
+
prefix = existing.empty? || existing.end_with?( "\n" ) ? '' : "\n"
|
|
113
|
+
File.open( value, 'a' ) { |f| f.puts "#{prefix}#{data}" }
|
|
114
|
+
end
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
#
|
|
@@ -117,7 +125,7 @@ module Gloo
|
|
|
117
125
|
expr = Gloo::Expr::Expression.new( @engine, @params.tokens )
|
|
118
126
|
data = expr.evaluate
|
|
119
127
|
end
|
|
120
|
-
File.write( value, data )
|
|
128
|
+
file_op( 'write' ) { File.write( value, data ) }
|
|
121
129
|
end
|
|
122
130
|
|
|
123
131
|
#
|
|
@@ -125,7 +133,8 @@ module Gloo
|
|
|
125
133
|
#
|
|
126
134
|
def msg_delete
|
|
127
135
|
return unless value
|
|
128
|
-
|
|
136
|
+
|
|
137
|
+
file_op( 'delete' ) { File.delete( value ) }
|
|
129
138
|
end
|
|
130
139
|
|
|
131
140
|
#
|
|
@@ -156,7 +165,7 @@ module Gloo
|
|
|
156
165
|
# Create a directory.
|
|
157
166
|
#
|
|
158
167
|
def msg_mkdir
|
|
159
|
-
FileUtils.mkdir_p(value) unless Dir.exist?(value)
|
|
168
|
+
file_op( 'make folder' ) { FileUtils.mkdir_p( value ) } unless Dir.exist?( value )
|
|
160
169
|
end
|
|
161
170
|
|
|
162
171
|
#
|
|
@@ -205,10 +214,10 @@ module Gloo
|
|
|
205
214
|
# Get the SHA256 hash of the file contents.
|
|
206
215
|
#
|
|
207
216
|
def msg_get_sha256
|
|
208
|
-
return unless check_file_exists?
|
|
217
|
+
return @engine.heap.it.set_to( false ) unless check_file_exists?
|
|
209
218
|
|
|
210
|
-
file_hash = FileHandle.hash_for_file( value )
|
|
211
|
-
@engine.heap.it.set_to file_hash
|
|
219
|
+
file_hash = file_op( 'read' ) { FileHandle.hash_for_file( value ) }
|
|
220
|
+
@engine.heap.it.set_to( file_hash || false )
|
|
212
221
|
end
|
|
213
222
|
|
|
214
223
|
#
|
|
@@ -221,18 +230,45 @@ module Gloo
|
|
|
221
230
|
return file_hash
|
|
222
231
|
end
|
|
223
232
|
|
|
233
|
+
#
|
|
234
|
+
# Run a file system operation. A failure the file system reports
|
|
235
|
+
# (no such file, a folder where a file was expected, no permission,
|
|
236
|
+
# ...) is a runtime error, and the result is nil.
|
|
237
|
+
#
|
|
238
|
+
def file_op( action )
|
|
239
|
+
return yield
|
|
240
|
+
rescue Errno::ENOENT
|
|
241
|
+
@engine.err Gloo::Core::NotFound.file( value )
|
|
242
|
+
return nil
|
|
243
|
+
rescue SystemCallError => e
|
|
244
|
+
@engine.err "Could not #{action} '#{value}': #{e.message}"
|
|
245
|
+
return nil
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
#
|
|
249
|
+
# Check that the file exists and is a file, not a folder.
|
|
250
|
+
# Report an error if not.
|
|
251
|
+
#
|
|
252
|
+
def check_is_file?
|
|
253
|
+
return false unless check_file_exists?
|
|
254
|
+
return true if File.file?( value )
|
|
255
|
+
|
|
256
|
+
@engine.err "'#{value}' is a folder, not a file."
|
|
257
|
+
return false
|
|
258
|
+
end
|
|
259
|
+
|
|
224
260
|
#
|
|
225
261
|
# Check to see if the file exists.
|
|
226
|
-
#
|
|
262
|
+
# Report an error if not.
|
|
227
263
|
#
|
|
228
264
|
def check_file_exists?
|
|
229
265
|
if value.blank?
|
|
230
|
-
@engine.
|
|
266
|
+
@engine.err FILE_NAME_ERR
|
|
231
267
|
return false
|
|
232
268
|
end
|
|
233
269
|
|
|
234
270
|
unless File.exist?( value )
|
|
235
|
-
@engine.
|
|
271
|
+
@engine.err Gloo::Core::NotFound.file( value )
|
|
236
272
|
return false
|
|
237
273
|
end
|
|
238
274
|
|
|
@@ -254,7 +290,7 @@ module Gloo
|
|
|
254
290
|
'on disk. The string value of the file object is the path ' \
|
|
255
291
|
'and name of the file.',
|
|
256
292
|
:messages => [
|
|
257
|
-
'read ({into.obj}) — Read the file and put the data in the specified object. If {into.obj} is not specified, the data will be in it.',
|
|
293
|
+
'read ({into.obj}) — Read the file and put the data in the specified object. If {into.obj} is not specified, the data will be in it. If {into.obj} does not exist, that is an error.',
|
|
258
294
|
'write ({from.obj}) — Write the data in {from.obj} into the file.',
|
|
259
295
|
'append ({data}) — Append the given data to the file as a new line. Adds a leading newline first if the file doesn\'t already end with one.',
|
|
260
296
|
'delete — Delete the file.',
|