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
|
@@ -8,6 +8,16 @@ module Gloo
|
|
|
8
8
|
module Convert
|
|
9
9
|
class Converter
|
|
10
10
|
|
|
11
|
+
# How each type reads in a warning: "'x' is not an integer".
|
|
12
|
+
TYPE_NAMES = {
|
|
13
|
+
'Integer' => 'an integer',
|
|
14
|
+
'Decimal' => 'a decimal number',
|
|
15
|
+
'Date' => 'a date',
|
|
16
|
+
'Time' => 'a time',
|
|
17
|
+
'DateTime' => 'a date and time'
|
|
18
|
+
}.freeze
|
|
19
|
+
NO_CONVERSION_ERR = 'There is no conversion to '.freeze
|
|
20
|
+
|
|
11
21
|
#
|
|
12
22
|
# Initializer.
|
|
13
23
|
#
|
|
@@ -23,12 +33,30 @@ module Gloo
|
|
|
23
33
|
# Convert the given value to the specified type,
|
|
24
34
|
# or if no conversion is available, revert to default.
|
|
25
35
|
#
|
|
36
|
+
# If the value isn't really of that type (eg. 'x' for an
|
|
37
|
+
# integer), the conversion is still the best guess, and a warning
|
|
38
|
+
# says what was guessed. A kind of value there's no converter for
|
|
39
|
+
# is also warned about, using the default. Converting to a type
|
|
40
|
+
# that isn't known is an error.
|
|
41
|
+
#
|
|
26
42
|
def convert( value, to_type, default = nil )
|
|
27
43
|
begin
|
|
28
44
|
name = "Gloo::Convert::#{value.class}To#{to_type}"
|
|
29
|
-
clazz =
|
|
45
|
+
clazz = find_converter( name )
|
|
46
|
+
unless TYPE_NAMES.key?( to_type )
|
|
47
|
+
@engine.err "#{NO_CONVERSION_ERR}'#{to_type}'."
|
|
48
|
+
return default
|
|
49
|
+
end
|
|
50
|
+
unless clazz
|
|
51
|
+
@engine.warn "'#{value}' is not #{TYPE_NAMES[ to_type ]}; " \
|
|
52
|
+
"using #{default.nil? ? 'no value' : default}."
|
|
53
|
+
return default
|
|
54
|
+
end
|
|
55
|
+
|
|
30
56
|
o = clazz.new
|
|
31
|
-
|
|
57
|
+
result = o.convert( value )
|
|
58
|
+
warn_bad_value( o, value, result, to_type )
|
|
59
|
+
return result
|
|
32
60
|
rescue => e
|
|
33
61
|
@engine.log_exception e
|
|
34
62
|
end
|
|
@@ -36,6 +64,39 @@ module Gloo
|
|
|
36
64
|
return default
|
|
37
65
|
end
|
|
38
66
|
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Find the converter class by name, or nil if there isn't one for
|
|
71
|
+
# that kind of value.
|
|
72
|
+
#
|
|
73
|
+
def find_converter( name )
|
|
74
|
+
return name.split( '::' ).inject( Object ) { |o, c| o.const_get( c, false ) }
|
|
75
|
+
rescue NameError
|
|
76
|
+
return nil
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
#
|
|
80
|
+
# Warn if the converter says the value isn't valid for its type.
|
|
81
|
+
#
|
|
82
|
+
def warn_bad_value( converter, value, result, to_type )
|
|
83
|
+
return unless converter.respond_to?( :valid? )
|
|
84
|
+
return if converter.valid?( value, result )
|
|
85
|
+
|
|
86
|
+
@engine.warn "'#{value}' is not #{TYPE_NAMES[ to_type ]}; " \
|
|
87
|
+
"using #{describe( converter, result )}."
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
#
|
|
91
|
+
# Describe the guess for a warning.
|
|
92
|
+
#
|
|
93
|
+
def describe( converter, result )
|
|
94
|
+
return 'no value' if result.nil?
|
|
95
|
+
return converter.describe( result ) if converter.respond_to?( :describe )
|
|
96
|
+
|
|
97
|
+
return result.to_s
|
|
98
|
+
end
|
|
99
|
+
|
|
39
100
|
end
|
|
40
101
|
end
|
|
41
102
|
end
|
|
@@ -21,6 +21,20 @@ module Gloo
|
|
|
21
21
|
return Chronic.parse( value )
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
#
|
|
25
|
+
# Is the value really a date? A blank value is no value.
|
|
26
|
+
#
|
|
27
|
+
def valid?( value, result )
|
|
28
|
+
return value.blank? || !result.nil?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
#
|
|
32
|
+
# Describe the guess for a warning.
|
|
33
|
+
#
|
|
34
|
+
def describe( result )
|
|
35
|
+
return result.strftime( Gloo::Objs::Date::DEFAULT_FORMAT )
|
|
36
|
+
end
|
|
37
|
+
|
|
24
38
|
end
|
|
25
39
|
end
|
|
26
40
|
end
|
|
@@ -16,6 +16,22 @@ module Gloo
|
|
|
16
16
|
return Chronic.parse( value )
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
#
|
|
20
|
+
# Is the value really a date and time? A blank value is no value.
|
|
21
|
+
#
|
|
22
|
+
def valid?( value, result )
|
|
23
|
+
return true if value.blank?
|
|
24
|
+
|
|
25
|
+
return !result.nil? && !StringToTime.clock_out_of_range?( value )
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
#
|
|
29
|
+
# Describe the guess for a warning.
|
|
30
|
+
#
|
|
31
|
+
def describe( result )
|
|
32
|
+
return result.strftime( Gloo::Objs::DateTime::DEFAULT_FORMAT )
|
|
33
|
+
end
|
|
34
|
+
|
|
19
35
|
end
|
|
20
36
|
end
|
|
21
37
|
end
|
|
@@ -15,6 +15,19 @@ module Gloo
|
|
|
15
15
|
return value.to_f
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
#
|
|
19
|
+
# Is the value really a decimal number? A blank value is no
|
|
20
|
+
# value (0.0).
|
|
21
|
+
#
|
|
22
|
+
def valid?( value, _result )
|
|
23
|
+
return true if value.blank?
|
|
24
|
+
|
|
25
|
+
Float( value.strip )
|
|
26
|
+
return true
|
|
27
|
+
rescue ArgumentError
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
|
|
18
31
|
end
|
|
19
32
|
end
|
|
20
33
|
end
|
|
@@ -12,11 +12,18 @@ module Gloo
|
|
|
12
12
|
# Convert the given string value to an integer.
|
|
13
13
|
#
|
|
14
14
|
def convert( value )
|
|
15
|
-
return
|
|
16
|
-
|
|
15
|
+
return 0 if value.blank?
|
|
16
|
+
|
|
17
17
|
return value.to_i
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
#
|
|
21
|
+
# Is the value really an integer? A blank value is no value (0).
|
|
22
|
+
#
|
|
23
|
+
def valid?( value, _result )
|
|
24
|
+
return value.blank? || value.match?( /\A\s*[-+]?\d+\s*\z/ )
|
|
25
|
+
end
|
|
26
|
+
|
|
20
27
|
end
|
|
21
28
|
end
|
|
22
29
|
end
|
|
@@ -16,6 +16,34 @@ module Gloo
|
|
|
16
16
|
return Chronic.parse( value )
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
#
|
|
20
|
+
# Is the value really a time? A blank value is no value. A clock
|
|
21
|
+
# time out of range (eg. 25:99) is parsed, but rolls over.
|
|
22
|
+
#
|
|
23
|
+
def valid?( value, result )
|
|
24
|
+
return true if value.blank?
|
|
25
|
+
|
|
26
|
+
return !result.nil? && !StringToTime.clock_out_of_range?( value )
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# Describe the guess for a warning.
|
|
31
|
+
#
|
|
32
|
+
def describe( result )
|
|
33
|
+
return result.strftime( Gloo::Objs::Time::DEFAULT_FORMAT )
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Does the value have a clock time (h:mm or h:mm:ss) with an hour,
|
|
38
|
+
# minute or second out of range?
|
|
39
|
+
#
|
|
40
|
+
def self.clock_out_of_range?( value )
|
|
41
|
+
m = value.match( /(\d{1,2}):(\d{2})(?::(\d{2}))?/ )
|
|
42
|
+
return false unless m
|
|
43
|
+
|
|
44
|
+
return m[ 1 ].to_i > 24 || m[ 2 ].to_i > 59 || m[ 3 ].to_i > 59
|
|
45
|
+
end
|
|
46
|
+
|
|
19
47
|
end
|
|
20
48
|
end
|
|
21
49
|
end
|
data/lib/gloo/core/dictionary.rb
CHANGED
|
@@ -27,10 +27,14 @@ module Gloo
|
|
|
27
27
|
#
|
|
28
28
|
# Get the singleton Dictionary and Initialize
|
|
29
29
|
# if this is the first time.
|
|
30
|
+
# The engine is optional -- verbs/objects self-register at class
|
|
31
|
+
# load time, before any engine (and its log) exists. When an
|
|
32
|
+
# engine is given, it's used to report a duplicate-registration
|
|
33
|
+
# error; without one, duplicates are just silently skipped.
|
|
30
34
|
#
|
|
31
|
-
def self.get
|
|
35
|
+
def self.get( engine=nil )
|
|
32
36
|
o = Gloo::Core::Dictionary.instance
|
|
33
|
-
o.init if o.verbs.count == 0
|
|
37
|
+
o.init( engine ) if o.verbs.count == 0
|
|
34
38
|
return o
|
|
35
39
|
end
|
|
36
40
|
|
|
@@ -51,9 +55,9 @@ module Gloo
|
|
|
51
55
|
#
|
|
52
56
|
# Initialize verbs and objects in the dictionary.
|
|
53
57
|
#
|
|
54
|
-
def init
|
|
55
|
-
init_verbs
|
|
56
|
-
init_objs
|
|
58
|
+
def init( engine=nil )
|
|
59
|
+
init_verbs( engine )
|
|
60
|
+
init_objs( engine )
|
|
57
61
|
end
|
|
58
62
|
|
|
59
63
|
#
|
|
@@ -145,15 +149,15 @@ module Gloo
|
|
|
145
149
|
#
|
|
146
150
|
# Register a verb after start up.
|
|
147
151
|
#
|
|
148
|
-
def register_verb_post_start( verb_class )
|
|
149
|
-
add_verb verb_class
|
|
152
|
+
def register_verb_post_start( verb_class, engine=nil )
|
|
153
|
+
add_verb verb_class, engine
|
|
150
154
|
end
|
|
151
155
|
|
|
152
156
|
#
|
|
153
157
|
# Register an object type after start up.
|
|
154
158
|
#
|
|
155
|
-
def register_obj_post_start( obj_class )
|
|
156
|
-
add_object obj_class
|
|
159
|
+
def register_obj_post_start( obj_class, engine=nil )
|
|
160
|
+
add_object obj_class, engine
|
|
157
161
|
end
|
|
158
162
|
|
|
159
163
|
#
|
|
@@ -200,28 +204,32 @@ module Gloo
|
|
|
200
204
|
#
|
|
201
205
|
# Init the list of objects.
|
|
202
206
|
#
|
|
203
|
-
def init_objs
|
|
207
|
+
def init_objs( engine=nil )
|
|
204
208
|
@obj_references.each do |o|
|
|
205
|
-
add_object o
|
|
209
|
+
add_object o, engine
|
|
206
210
|
end
|
|
207
211
|
end
|
|
208
212
|
|
|
209
213
|
#
|
|
210
214
|
# Init the list of verbs.
|
|
211
215
|
#
|
|
212
|
-
def init_verbs
|
|
216
|
+
def init_verbs( engine=nil )
|
|
213
217
|
@verb_references.each do |v|
|
|
214
|
-
add_verb v
|
|
218
|
+
add_verb v, engine
|
|
215
219
|
end
|
|
216
220
|
end
|
|
217
221
|
|
|
218
|
-
#
|
|
219
|
-
# Add an object to the dictionary
|
|
220
|
-
#
|
|
221
|
-
|
|
222
|
+
#
|
|
223
|
+
# Add an object to the dictionary.
|
|
224
|
+
# The engine is optional and is only used to report a
|
|
225
|
+
# duplicate-registration error -- when it's not given (e.g.
|
|
226
|
+
# self-registration at class load time), duplicates are just
|
|
227
|
+
# silently skipped.
|
|
228
|
+
#
|
|
229
|
+
def add_object( obj, engine=nil )
|
|
222
230
|
# Make sure it hasn't already been registered
|
|
223
231
|
if ( @objs.key?(obj.typename) || @objs.key?(obj.short_typename) )
|
|
224
|
-
|
|
232
|
+
engine&.err "duplicate object type '#{obj.typename}' or '#{obj.short_typename}'"
|
|
225
233
|
return
|
|
226
234
|
end
|
|
227
235
|
|
|
@@ -232,12 +240,13 @@ module Gloo
|
|
|
232
240
|
end
|
|
233
241
|
|
|
234
242
|
#
|
|
235
|
-
# Add a verb to the dictionary
|
|
243
|
+
# Add a verb to the dictionary.
|
|
244
|
+
# The engine is optional -- see add_object.
|
|
236
245
|
#
|
|
237
|
-
def add_verb( verb )
|
|
246
|
+
def add_verb( verb, engine=nil )
|
|
238
247
|
# Make sure it hasn't already been registered
|
|
239
248
|
if ( verb?( verb.keyword ) || verb?( verb.keyword_shortcut ) )
|
|
240
|
-
|
|
249
|
+
engine&.err "duplicate verb keyword '#{verb.keyword}' or '#{verb.keyword_shortcut}'"
|
|
241
250
|
return
|
|
242
251
|
end
|
|
243
252
|
|
data/lib/gloo/core/error.rb
CHANGED
|
@@ -8,7 +8,12 @@ module Gloo
|
|
|
8
8
|
module Core
|
|
9
9
|
class Error
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# The kind of error: the command couldn't be understood (syntax),
|
|
12
|
+
# or it was understood but couldn't be done (runtime).
|
|
13
|
+
SYNTAX = 'syntax'.freeze
|
|
14
|
+
RUNTIME = 'runtime'.freeze
|
|
15
|
+
|
|
16
|
+
attr_accessor :value, :error_count, :kind, :location
|
|
12
17
|
|
|
13
18
|
#
|
|
14
19
|
# Set up the error object.
|
|
@@ -23,14 +28,18 @@ module Gloo
|
|
|
23
28
|
def clear
|
|
24
29
|
@error_count = 0
|
|
25
30
|
@value = nil
|
|
31
|
+
@kind = nil
|
|
32
|
+
@location = nil
|
|
26
33
|
end
|
|
27
34
|
|
|
28
35
|
#
|
|
29
36
|
# Set the value of error.
|
|
30
37
|
#
|
|
31
|
-
def set_to( new_value )
|
|
38
|
+
def set_to( new_value, kind = RUNTIME, location = nil )
|
|
32
39
|
@error_count += 1
|
|
33
40
|
@value = new_value
|
|
41
|
+
@kind = kind
|
|
42
|
+
@location = location
|
|
34
43
|
end
|
|
35
44
|
|
|
36
45
|
#
|
|
@@ -75,16 +75,19 @@ module Gloo
|
|
|
75
75
|
#
|
|
76
76
|
# Run the on_error scripts in any open objects.
|
|
77
77
|
# For each on_error script found, look for the error_data container
|
|
78
|
-
# and set the error message
|
|
78
|
+
# and set the error message, backtrace, kind (syntax or runtime)
|
|
79
|
+
# and location -- each only if error_data has that child.
|
|
79
80
|
#
|
|
80
|
-
def on_error msg, backtrace
|
|
81
|
+
def on_error msg, backtrace, kind = Gloo::Core::Error::RUNTIME, location = nil
|
|
81
82
|
@engine.log.debug 'on_error event'
|
|
82
83
|
arr = Gloo::Core::ObjFinder.by_name( @engine, 'on_error' )
|
|
83
84
|
arr.each do |o|
|
|
84
85
|
data = o.parent.find_child 'error_data'
|
|
85
86
|
if data
|
|
86
|
-
data.find_child( 'message' )
|
|
87
|
-
data.find_child( 'backtrace' )
|
|
87
|
+
data.find_child( 'message' )&.set_value msg
|
|
88
|
+
data.find_child( 'backtrace' )&.set_value backtrace
|
|
89
|
+
data.find_child( 'kind' )&.set_value kind
|
|
90
|
+
data.find_child( 'location' )&.set_value location
|
|
88
91
|
end
|
|
89
92
|
|
|
90
93
|
Gloo::Exec::Dispatch.message( @engine, 'run', o )
|
data/lib/gloo/core/factory.rb
CHANGED
|
@@ -8,6 +8,9 @@ module Gloo
|
|
|
8
8
|
module Core
|
|
9
9
|
class Factory < Baseo
|
|
10
10
|
|
|
11
|
+
UNTYPED = 'untyped'.freeze
|
|
12
|
+
UNKNOWN_TYPE_ERR = 'Unknown type '.freeze
|
|
13
|
+
|
|
11
14
|
# ---------------------------------------------------------------------
|
|
12
15
|
# Initializer
|
|
13
16
|
# ---------------------------------------------------------------------
|
|
@@ -155,6 +158,12 @@ module Gloo
|
|
|
155
158
|
return self.update_existing pn, params[ :value ]
|
|
156
159
|
end
|
|
157
160
|
|
|
161
|
+
unless parent
|
|
162
|
+
missing = pn.elements[ 0..-2 ].join( '.' )
|
|
163
|
+
@engine.err "Could not create '#{params[ :name ]}': #{Gloo::Core::NotFound.object( missing )}"
|
|
164
|
+
return nil
|
|
165
|
+
end
|
|
166
|
+
|
|
158
167
|
@engine.log.debug "Creating new object: #{obj_name}"
|
|
159
168
|
return create_new obj_name, params[ :value ], objtype, parent
|
|
160
169
|
end
|
|
@@ -163,11 +172,6 @@ module Gloo
|
|
|
163
172
|
# Create a new object.
|
|
164
173
|
#
|
|
165
174
|
def create_new( name, value, type, parent )
|
|
166
|
-
unless parent
|
|
167
|
-
@engine.err "Could not create object. Bad path: #{name}"
|
|
168
|
-
return nil
|
|
169
|
-
end
|
|
170
|
-
|
|
171
175
|
# Check to see if the object exists already
|
|
172
176
|
return parent.find_child( name ) if parent.contains_child?( name )
|
|
173
177
|
|
|
@@ -189,20 +193,21 @@ module Gloo
|
|
|
189
193
|
|
|
190
194
|
#
|
|
191
195
|
# Find the object type by name.
|
|
192
|
-
#
|
|
193
|
-
#
|
|
196
|
+
# An unknown type is a syntax error; the best guess is untyped,
|
|
197
|
+
# so the object (and anything nested under it) is still created.
|
|
198
|
+
# Return nil if the object type cannot be created.
|
|
194
199
|
#
|
|
195
200
|
def find_type( type_name )
|
|
196
|
-
type_name =
|
|
201
|
+
type_name = UNTYPED if type_name.nil? || type_name.strip.empty?
|
|
197
202
|
t = @engine.dictionary.find_obj( type_name )
|
|
198
203
|
|
|
199
204
|
if t.nil?
|
|
200
|
-
@engine.
|
|
201
|
-
|
|
205
|
+
@engine.syntax_err "#{UNKNOWN_TYPE_ERR}'#{type_name}'; using untyped."
|
|
206
|
+
t = @engine.dictionary.find_obj( UNTYPED )
|
|
202
207
|
end
|
|
203
208
|
|
|
204
209
|
unless t.can_create?
|
|
205
|
-
@engine.
|
|
210
|
+
@engine.syntax_err "'#{type_name}' cannot be created."
|
|
206
211
|
return nil
|
|
207
212
|
end
|
|
208
213
|
|
data/lib/gloo/core/here.rb
CHANGED
|
@@ -23,11 +23,16 @@ module Gloo
|
|
|
23
23
|
#
|
|
24
24
|
def self.expand_here( engine, pn )
|
|
25
25
|
target = engine.exec_env.here_obj
|
|
26
|
+
# Outside a running script there's nothing for ^ to be
|
|
27
|
+
# relative to; leave the path as is, so the lookup finds nothing.
|
|
28
|
+
return unless target
|
|
26
29
|
|
|
27
30
|
here = pn.elements.first
|
|
28
31
|
remainder = pn.elements[ 1..-1 ].join( '.' )
|
|
29
32
|
|
|
30
|
-
here.length.times { target = target
|
|
33
|
+
here.length.times { target = target&.parent }
|
|
34
|
+
return unless target
|
|
35
|
+
|
|
31
36
|
full_path = target.pn.empty? ? remainder : "#{target.pn}.#{remainder}"
|
|
32
37
|
pn.set_to full_path
|
|
33
38
|
end
|
data/lib/gloo/core/invoker.rb
CHANGED
|
@@ -12,7 +12,6 @@ module Gloo
|
|
|
12
12
|
class Invoker
|
|
13
13
|
|
|
14
14
|
NO_TARGET_ERR = 'Missing function reference!'.freeze
|
|
15
|
-
NOT_FOUND_ERR = 'Object was not found: '.freeze
|
|
16
15
|
NOT_FUNCTION_ERR = 'Not a function: '.freeze
|
|
17
16
|
PARAM_COUNT_ERR = 'Wrong number of parameters for function: '.freeze
|
|
18
17
|
|
|
@@ -26,7 +25,7 @@ module Gloo
|
|
|
26
25
|
#
|
|
27
26
|
def self.invoke( engine, target, arg_tokens )
|
|
28
27
|
if target.nil? || target.to_s.strip.empty?
|
|
29
|
-
engine.
|
|
28
|
+
engine.syntax_err NO_TARGET_ERR
|
|
30
29
|
return nil
|
|
31
30
|
end
|
|
32
31
|
|
|
@@ -49,7 +48,7 @@ module Gloo
|
|
|
49
48
|
func = pn.resolve
|
|
50
49
|
|
|
51
50
|
unless func
|
|
52
|
-
engine.err
|
|
51
|
+
engine.err Gloo::Core::NotFound.object( target )
|
|
53
52
|
return nil
|
|
54
53
|
end
|
|
55
54
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Author:: Eric Crane (mailto:eric.crane@mac.com)
|
|
2
|
+
# Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# The one wording for "not found" errors, so every command reports
|
|
5
|
+
# a missing object, file, folder or verb the same way.
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
module Gloo
|
|
9
|
+
module Core
|
|
10
|
+
module NotFound
|
|
11
|
+
|
|
12
|
+
#
|
|
13
|
+
# An object path that doesn't resolve, shown as written.
|
|
14
|
+
#
|
|
15
|
+
def self.object( path )
|
|
16
|
+
return "Object '#{path}' was not found."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#
|
|
20
|
+
# A file that doesn't exist.
|
|
21
|
+
#
|
|
22
|
+
def self.file( name )
|
|
23
|
+
return "File '#{name}' was not found."
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# A folder that doesn't exist.
|
|
28
|
+
#
|
|
29
|
+
def self.folder( name )
|
|
30
|
+
return "Folder '#{name}' was not found."
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# A verb that isn't in the dictionary.
|
|
35
|
+
#
|
|
36
|
+
def self.verb( name )
|
|
37
|
+
return "Verb '#{name}' was not found."
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/gloo/core/obj.rb
CHANGED
|
@@ -241,7 +241,7 @@ module Gloo
|
|
|
241
241
|
#
|
|
242
242
|
def contains_child?( name )
|
|
243
243
|
@children.each do |o|
|
|
244
|
-
return true if name.downcase == o.name.downcase
|
|
244
|
+
return true if name.to_s.downcase == o.name.downcase
|
|
245
245
|
end
|
|
246
246
|
return false
|
|
247
247
|
end
|
|
@@ -261,7 +261,7 @@ module Gloo
|
|
|
261
261
|
if is_alias?
|
|
262
262
|
ln = Gloo::Core::Pn.new( @engine, self.value )
|
|
263
263
|
redirect = ln.resolve
|
|
264
|
-
return redirect
|
|
264
|
+
return redirect&.find_child( name )
|
|
265
265
|
end
|
|
266
266
|
return nil
|
|
267
267
|
end
|
|
@@ -349,6 +349,29 @@ module Gloo
|
|
|
349
349
|
return msgs.include?( msg.strip.downcase )
|
|
350
350
|
end
|
|
351
351
|
|
|
352
|
+
#
|
|
353
|
+
# Run a step of a message that can fail on ordinary bad input or
|
|
354
|
+
# the outside world (invalid JSON, an unknown host, a wrong key).
|
|
355
|
+
# Returns true if the block ran. If it raises one of the given
|
|
356
|
+
# exception classes, report it as a gloo error ("Could not
|
|
357
|
+
# {action}: {reason}"), put false into it (unless set_it is
|
|
358
|
+
# false, for an action that doesn't set it), and return false.
|
|
359
|
+
# Any other exception is left alone, so a real bug still shows
|
|
360
|
+
# up as one.
|
|
361
|
+
#
|
|
362
|
+
# return false unless attempt( 'parse the JSON', JSON::ParserError ) do
|
|
363
|
+
# json = JSON.parse( value )
|
|
364
|
+
# end
|
|
365
|
+
#
|
|
366
|
+
def attempt( action, *errors, set_it: true )
|
|
367
|
+
yield
|
|
368
|
+
return true
|
|
369
|
+
rescue *errors => e
|
|
370
|
+
@engine.err "Could not #{action}: #{e.message}"
|
|
371
|
+
@engine.heap.it.set_to false if set_it
|
|
372
|
+
return false
|
|
373
|
+
end
|
|
374
|
+
|
|
352
375
|
#
|
|
353
376
|
# Sent this object the given message.
|
|
354
377
|
#
|
|
@@ -356,7 +379,9 @@ module Gloo
|
|
|
356
379
|
@params = params
|
|
357
380
|
return self.dispatch msg if self.can_receive_message? msg
|
|
358
381
|
|
|
382
|
+
# The message never ran, so there is no result.
|
|
359
383
|
@engine.err "Object #{self.name} cannot receive message #{msg}"
|
|
384
|
+
@engine.heap.it.set_to false
|
|
360
385
|
return false
|
|
361
386
|
end
|
|
362
387
|
|
|
@@ -370,6 +395,7 @@ module Gloo
|
|
|
370
395
|
return true
|
|
371
396
|
else
|
|
372
397
|
@engine.err "Message #{msg} not implemented"
|
|
398
|
+
@engine.heap.it.set_to false
|
|
373
399
|
return false
|
|
374
400
|
end
|
|
375
401
|
end
|
data/lib/gloo/core/op.rb
CHANGED
|
@@ -15,6 +15,7 @@ module Gloo
|
|
|
15
15
|
def self.op?( token )
|
|
16
16
|
return [
|
|
17
17
|
Gloo::Expr::OpPlus::SYMBOL,
|
|
18
|
+
Gloo::Expr::OpPlus::ALT_SYMBOL,
|
|
18
19
|
Gloo::Expr::OpMinus::SYMBOL,
|
|
19
20
|
Gloo::Expr::OpMult::SYMBOL,
|
|
20
21
|
Gloo::Expr::OpDiv::SYMBOL,
|
|
@@ -34,6 +35,7 @@ module Gloo
|
|
|
34
35
|
def self.create_op( token )
|
|
35
36
|
case token
|
|
36
37
|
when Gloo::Expr::OpPlus::SYMBOL then Gloo::Expr::OpPlus.new
|
|
38
|
+
when Gloo::Expr::OpPlus::ALT_SYMBOL then Gloo::Expr::OpPlus.new
|
|
37
39
|
when Gloo::Expr::OpMinus::SYMBOL then Gloo::Expr::OpMinus.new
|
|
38
40
|
when Gloo::Expr::OpMult::SYMBOL then Gloo::Expr::OpMult.new
|
|
39
41
|
when Gloo::Expr::OpDiv::SYMBOL then Gloo::Expr::OpDiv.new
|
data/lib/gloo/core/parser.rb
CHANGED