lazy_graph 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +282 -47
- data/Rakefile +12 -1
- data/examples/converter.rb +41 -0
- data/examples/performance_tests.rb +9 -9
- data/examples/shopping_cart_totals.rb +56 -0
- data/lib/lazy_graph/builder/dsl.rb +67 -30
- data/lib/lazy_graph/builder.rb +52 -25
- data/lib/lazy_graph/builder_group.rb +31 -18
- data/lib/lazy_graph/cli.rb +47 -0
- data/lib/lazy_graph/context.rb +37 -12
- data/lib/lazy_graph/environment.rb +6 -0
- data/lib/lazy_graph/graph.rb +28 -12
- data/lib/lazy_graph/hash_utils.rb +10 -5
- data/lib/lazy_graph/logger.rb +87 -0
- data/lib/lazy_graph/missing_value.rb +9 -2
- data/lib/lazy_graph/node/array_node.rb +14 -18
- data/lib/lazy_graph/node/derived_rules.rb +72 -17
- data/lib/lazy_graph/node/node_properties.rb +34 -9
- data/lib/lazy_graph/node/object_node.rb +41 -56
- data/lib/lazy_graph/node/symbol_hash.rb +15 -2
- data/lib/lazy_graph/node.rb +183 -94
- data/lib/lazy_graph/path_parser/path.rb +15 -7
- data/lib/lazy_graph/path_parser/path_group.rb +6 -0
- data/lib/lazy_graph/path_parser/path_part.rb +8 -0
- data/lib/lazy_graph/path_parser.rb +1 -1
- data/lib/lazy_graph/rack_app.rb +138 -0
- data/lib/lazy_graph/rack_server.rb +199 -0
- data/lib/lazy_graph/stack_pointer.rb +22 -14
- data/lib/lazy_graph/version.rb +1 -1
- data/lib/lazy_graph.rb +6 -8
- metadata +115 -11
- data/lib/lazy_graph/server.rb +0 -91
@@ -39,6 +39,8 @@ module LazyGraph
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def set_pattern_property(pattern, value)
|
42
|
+
raise 'Trying to set a property without a name' unless pattern.is_a?(String) || pattern.is_a?(Symbol)
|
43
|
+
|
42
44
|
pattern = pattern.to_sym
|
43
45
|
properties = schema[:patternProperties] ||= {}
|
44
46
|
properties[pattern] = \
|
@@ -51,6 +53,8 @@ module LazyGraph
|
|
51
53
|
end
|
52
54
|
|
53
55
|
def set_property(key, value)
|
56
|
+
raise 'Trying to set a property without a name' unless key.is_a?(String) || key.is_a?(Symbol)
|
57
|
+
|
54
58
|
key = key.to_sym
|
55
59
|
properties = schema[:properties] ||= {}
|
56
60
|
properties[key] = \
|
@@ -63,8 +67,8 @@ module LazyGraph
|
|
63
67
|
end
|
64
68
|
|
65
69
|
def object_conditional(
|
66
|
-
name = nil, required: false, pattern_property: false, rule: nil,
|
67
|
-
default: nil, description: nil, **opts, &blk
|
70
|
+
name = nil, required: false, pattern_property: false, rule: nil, copy: nil,
|
71
|
+
default: nil, description: nil, extend: nil, conditions: nil, **opts, &blk
|
68
72
|
)
|
69
73
|
new_object = {
|
70
74
|
type: :object,
|
@@ -72,20 +76,24 @@ module LazyGraph
|
|
72
76
|
additionalProperties: false,
|
73
77
|
**(!default.nil? ? { default: default } : {}),
|
74
78
|
**(description ? { description: description } : {}),
|
79
|
+
validate_presence: required,
|
75
80
|
**opts
|
76
81
|
}
|
77
82
|
@prev_match_cases = @match_cases
|
78
83
|
@match_cases = []
|
79
|
-
|
80
|
-
|
84
|
+
yields(new_object, &lambda {
|
85
|
+
blk&.call
|
86
|
+
extend&.call
|
87
|
+
})
|
81
88
|
|
82
89
|
object_names = @match_cases.map do |match_case|
|
83
90
|
rule = rule_from_when(match_case[:when_clause])
|
84
|
-
set_property(match_case[:name],
|
91
|
+
set_property(match_case[:name],
|
92
|
+
{ type: :object, rule: rule, rule_location: rule_location, **match_case[:schema] })
|
85
93
|
match_case[:name]
|
86
94
|
end
|
87
95
|
|
88
|
-
new_object[:rule] = rule_from_first_of(object_names)
|
96
|
+
new_object[:rule] = rule_from_first_of(object_names, conditions)
|
89
97
|
@match_cases = @prev_match_cases
|
90
98
|
required(name) if required && default.nil? && rule.nil?
|
91
99
|
pattern_property ? set_pattern_property(name, new_object) : set_property(name, new_object)
|
@@ -96,23 +104,32 @@ module LazyGraph
|
|
96
104
|
yields(@match_cases.last[:schema], &blk)
|
97
105
|
end
|
98
106
|
|
107
|
+
def rule_location
|
108
|
+
@dir ||= File.expand_path(File.dirname(__FILE__), '../../../../')
|
109
|
+
caller.find { |c| !c.include?(@dir) }.split(':').first(2)
|
110
|
+
end
|
111
|
+
|
99
112
|
def object(
|
100
|
-
name = nil, required: false, pattern_property: false, rule: nil,
|
101
|
-
default: nil, description: nil, **opts, &blk
|
113
|
+
name = nil, required: false, pattern_property: false, rule: nil, copy: nil,
|
114
|
+
default: nil, description: nil, extend: nil, **opts, &blk
|
102
115
|
)
|
103
116
|
rule ||= rule_from_when(opts.delete(:when)) if opts[:when]
|
104
117
|
rule ||= rule_from_first_of(opts.delete(:first_of)) if opts[:first_of]
|
118
|
+
rule ||= rule_from_copy(copy)
|
105
119
|
new_object = {
|
106
120
|
type: :object,
|
107
121
|
properties: {},
|
108
122
|
additionalProperties: false,
|
109
|
-
|
110
123
|
**(!default.nil? ? { default: default } : {}),
|
111
124
|
**(description ? { description: description } : {}),
|
112
|
-
**(rule ? { rule: rule } : {}),
|
125
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
126
|
+
validate_presence: required,
|
113
127
|
**opts
|
114
128
|
}
|
115
|
-
yields(new_object, &
|
129
|
+
yields(new_object, &lambda {
|
130
|
+
blk&.call
|
131
|
+
extend&.call
|
132
|
+
})
|
116
133
|
required(name) if required && default.nil? && rule.nil?
|
117
134
|
pattern_property ? set_pattern_property(name, new_object) : set_property(name, new_object)
|
118
135
|
end
|
@@ -120,14 +137,15 @@ module LazyGraph
|
|
120
137
|
def primitive(
|
121
138
|
name = nil, type, required: false, pattern_property: false,
|
122
139
|
default: nil, description: nil, enum: nil,
|
123
|
-
rule: nil, **additional_options, &blk
|
140
|
+
rule: nil, copy: nil, **additional_options, &blk
|
124
141
|
)
|
142
|
+
rule ||= rule_from_copy(copy)
|
125
143
|
new_primitive = {
|
126
144
|
type: type,
|
127
145
|
**(enum ? { enum: enum } : {}),
|
128
146
|
**(!default.nil? ? { default: default } : {}),
|
129
147
|
**(description ? { description: description } : {}),
|
130
|
-
**(rule ? { rule: rule } : {}),
|
148
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
131
149
|
**additional_options
|
132
150
|
}
|
133
151
|
yields(new_primitive, &blk)
|
@@ -135,8 +153,9 @@ module LazyGraph
|
|
135
153
|
pattern_property ? set_pattern_property(name, new_primitive) : set_property(name, new_primitive)
|
136
154
|
end
|
137
155
|
|
138
|
-
def decimal(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
|
156
|
+
def decimal(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, copy: nil,
|
139
157
|
**opts, &blk)
|
158
|
+
rule ||= rule_from_copy(copy)
|
140
159
|
# Define the decimal schema supporting multiple formats
|
141
160
|
new_decimal = {
|
142
161
|
anyOf: [
|
@@ -157,7 +176,8 @@ module LazyGraph
|
|
157
176
|
type: :decimal,
|
158
177
|
**(!default.nil? ? { default: default } : {}),
|
159
178
|
**(description ? { description: description } : {}),
|
160
|
-
**(rule ? { rule: rule } : {}),
|
179
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
180
|
+
validate_presence: required,
|
161
181
|
**opts
|
162
182
|
}
|
163
183
|
yields(new_decimal, &blk)
|
@@ -165,8 +185,9 @@ module LazyGraph
|
|
165
185
|
pattern_property ? set_pattern_property(name, new_decimal) : set_property(name, new_decimal)
|
166
186
|
end
|
167
187
|
|
168
|
-
def timestamp(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
|
188
|
+
def timestamp(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, copy: nil,
|
169
189
|
**opts, &blk)
|
190
|
+
rule ||= rule_from_copy(copy)
|
170
191
|
new_timestamp = {
|
171
192
|
anyOf: [
|
172
193
|
{
|
@@ -184,7 +205,8 @@ module LazyGraph
|
|
184
205
|
type: :timestamp, # Custom extended type
|
185
206
|
**(!default.nil? ? { default: default } : {}),
|
186
207
|
**(description ? { description: description } : {}),
|
187
|
-
**(rule ? { rule: rule } : {}),
|
208
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
209
|
+
validate_presence: required,
|
188
210
|
**opts
|
189
211
|
}
|
190
212
|
yields(new_timestamp, &blk)
|
@@ -192,15 +214,17 @@ module LazyGraph
|
|
192
214
|
pattern_property ? set_pattern_property(name, new_timestamp) : set_property(name, new_timestamp)
|
193
215
|
end
|
194
216
|
|
195
|
-
def time(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
|
217
|
+
def time(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, copy: nil,
|
196
218
|
**opts, &blk)
|
219
|
+
rule ||= rule_from_copy(copy)
|
197
220
|
new_time = {
|
198
221
|
type: :time, # Custom extended type
|
199
222
|
# Matches HH:mm[:ss[.SSS]]
|
200
223
|
pattern: '^\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,3})?)?$',
|
201
224
|
**(!default.nil? ? { default: default } : {}),
|
202
225
|
**(description ? { description: description } : {}),
|
203
|
-
**(rule ? { rule: rule } : {}),
|
226
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
227
|
+
validate_presence: required,
|
204
228
|
**opts
|
205
229
|
}
|
206
230
|
yields(new_time, &blk)
|
@@ -208,8 +232,9 @@ module LazyGraph
|
|
208
232
|
pattern_property ? set_pattern_property(name, new_time) : set_property(name, new_time)
|
209
233
|
end
|
210
234
|
|
211
|
-
def date(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
|
235
|
+
def date(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, copy: nil,
|
212
236
|
**opts, &blk)
|
237
|
+
rule ||= rule_from_copy(copy)
|
213
238
|
new_date = {
|
214
239
|
anyOf: [
|
215
240
|
{
|
@@ -221,7 +246,8 @@ module LazyGraph
|
|
221
246
|
type: :date, # Custom extended type
|
222
247
|
**(!default.nil? ? { default: default } : {}),
|
223
248
|
**(description ? { description: description } : {}),
|
224
|
-
**(rule ? { rule: rule } : {}),
|
249
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
250
|
+
validate_presence: required,
|
225
251
|
**opts
|
226
252
|
}
|
227
253
|
yields(new_date, &blk)
|
@@ -232,10 +258,11 @@ module LazyGraph
|
|
232
258
|
%i[boolean string const integer number null].each do |type|
|
233
259
|
define_method(type) do |
|
234
260
|
name = nil, required: false, pattern_property: false,
|
235
|
-
default: nil, description: nil, enum: nil, rule: nil,
|
261
|
+
default: nil, description: nil, enum: nil, rule: nil, copy: nil,
|
262
|
+
**additional_options, &blk|
|
236
263
|
primitive(
|
237
264
|
name, type, required: required, pattern_property: pattern_property, enum: enum,
|
238
|
-
default: default, rule: rule, description: description,
|
265
|
+
default: default, rule: rule, rule_location: rule_location, description: description,
|
239
266
|
**additional_options, &blk
|
240
267
|
)
|
241
268
|
end
|
@@ -253,17 +280,18 @@ module LazyGraph
|
|
253
280
|
schema[:anyOf] = (schema[:any_of] || []).concat(any_of).uniq
|
254
281
|
end
|
255
282
|
|
256
|
-
def array(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil,
|
257
|
-
type: :object, **opts, &
|
283
|
+
def array(name = nil, required: false, pattern_property: false, default: nil, description: nil, rule: nil, copy: nil,
|
284
|
+
type: :object, extend: nil, **opts, &block)
|
285
|
+
rule ||= rule_from_copy(copy)
|
258
286
|
new_array = {
|
259
287
|
type: :array,
|
260
288
|
**(!default.nil? ? { default: default } : {}),
|
261
289
|
**(description ? { description: description } : {}),
|
262
|
-
**(rule ? { rule: rule } : {}),
|
290
|
+
**(rule ? { rule: rule, rule_location: rule_location } : {}),
|
263
291
|
**opts,
|
264
292
|
items: { properties: {} }.tap do |items|
|
265
293
|
yields(items) do
|
266
|
-
send(type, :items, &
|
294
|
+
send(type, :items, extend: extend, &block)
|
267
295
|
end
|
268
296
|
end[:properties][:items]
|
269
297
|
}
|
@@ -276,6 +304,7 @@ module LazyGraph
|
|
276
304
|
end
|
277
305
|
|
278
306
|
def yields(other)
|
307
|
+
raise ArgumentError, 'Builder DSL used outside of rules module' unless schema
|
279
308
|
return unless block_given?
|
280
309
|
|
281
310
|
prev_schema = schema
|
@@ -284,6 +313,12 @@ module LazyGraph
|
|
284
313
|
self.schema = prev_schema
|
285
314
|
end
|
286
315
|
|
316
|
+
def rule_from_copy(copy)
|
317
|
+
return unless copy
|
318
|
+
|
319
|
+
"${#{copy}}"
|
320
|
+
end
|
321
|
+
|
287
322
|
def rule_from_when(when_clause)
|
288
323
|
inputs = when_clause.keys
|
289
324
|
conditions = when_clause
|
@@ -296,17 +331,19 @@ module LazyGraph
|
|
296
331
|
}
|
297
332
|
end
|
298
333
|
|
299
|
-
def rule_from_first_of(prop_list)
|
334
|
+
def rule_from_first_of(prop_list, conditions = nil)
|
335
|
+
prop_list += conditions.keys if conditions
|
300
336
|
{
|
301
337
|
inputs: prop_list,
|
302
|
-
calc: "itself.get_first_of(:#{prop_list.join(', :')})"
|
338
|
+
calc: "itself.get_first_of(:#{prop_list.join(', :')})",
|
339
|
+
**(conditions ? { conditions: conditions } : {})
|
303
340
|
}
|
304
341
|
end
|
305
342
|
|
306
343
|
def depends_on(*dependencies)
|
307
344
|
@resolved_dependencies ||= Hash.new do |h, k|
|
308
|
-
send(k) # Load dependency once
|
309
345
|
h[k] = true
|
346
|
+
send(k) # Load dependency once
|
310
347
|
end
|
311
348
|
dependencies.each(&@resolved_dependencies.method(:[]))
|
312
349
|
end
|
data/lib/lazy_graph/builder.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
# Subclass LazyGraph::Builder to create new builder classes
|
3
4
|
# which can be used to easily build a rule-set to be used as a LazyGraph.
|
4
5
|
#
|
@@ -6,15 +7,14 @@ require_relative 'builder/dsl'
|
|
6
7
|
|
7
8
|
module LazyGraph
|
8
9
|
class Builder
|
9
|
-
|
10
10
|
# Cache up to a fixed number of graphs, context and queries
|
11
11
|
BUILD_CACHE_CONFIG = {
|
12
12
|
# Store up to 1000 graphs
|
13
|
-
graph: {size: 1000, cache: {}},
|
13
|
+
graph: { size: ENV.fetch('LAZY_GRAPH_GRAPH_CACHE_MAX_ENTRIES', 1000).to_i, cache: {} },
|
14
14
|
# Store up to 5000 configs
|
15
|
-
context: {size: 5000, cache: {}},
|
15
|
+
context: { size: ENV.fetch('LAZY_GRAPH_CONTEXT_CACHE_MAX_ENTRIES', 5000).to_i, cache: {} },
|
16
16
|
# Store up to 5000 queries
|
17
|
-
query: {size: 5000, cache: {}}
|
17
|
+
query: { size: ENV.fetch('LAZY_GRAPH_QUERY_CACHE_MAX_ENTRIES', 5000).to_i, cache: {} }
|
18
18
|
}.compare_by_identity.freeze
|
19
19
|
|
20
20
|
include DSL
|
@@ -23,15 +23,25 @@ module LazyGraph
|
|
23
23
|
attr_accessor :schema
|
24
24
|
|
25
25
|
def initialize(schema: { type: 'object', properties: {} }) = @schema = schema
|
26
|
-
def
|
27
|
-
|
28
|
-
def eval!(context, *value, debug: false,
|
26
|
+
def context(value, debug: false, validate: true) = build!(debug: debug, validate: validate).context(value)
|
27
|
+
|
28
|
+
def eval!(context, *value, debug: false,
|
29
|
+
validate: true) = context(context, validate: validate, debug: debug).get(*value)
|
30
|
+
alias feed context
|
31
|
+
|
32
|
+
def build!(debug: false, validate: true) = @schema.to_lazy_graph(
|
33
|
+
debug: debug,
|
34
|
+
validate: validate,
|
35
|
+
helpers: self.class.helper_modules,
|
36
|
+
namespace: self.class
|
37
|
+
)
|
29
38
|
|
30
39
|
def self.rules_module(name, schema = { type: 'object', properties: {} }, &blk)
|
31
40
|
rules_modules[:properties][name.to_sym] = { type: :object, properties: schema }
|
32
41
|
module_body_func_name = :"_#{name}"
|
33
42
|
define_method(module_body_func_name, &blk)
|
34
43
|
define_method(name) do |**args, &inner_blk|
|
44
|
+
@path = @path.nil? ? "#{name}" : "#{@path}+#{name}" unless @path.to_s.include?(name.to_s)
|
35
45
|
send(module_body_func_name, **args, &inner_blk)
|
36
46
|
self
|
37
47
|
end
|
@@ -52,6 +62,8 @@ module LazyGraph
|
|
52
62
|
attr_reader :helper_modules
|
53
63
|
end
|
54
64
|
|
65
|
+
def self.register_helper_modules(*mods) = mods.each(&method(:register_helper_module))
|
66
|
+
|
55
67
|
def self.register_helper_module(mod)
|
56
68
|
(@helper_modules ||= []) << mod
|
57
69
|
end
|
@@ -64,6 +76,12 @@ module LazyGraph
|
|
64
76
|
@helper_modules = nil
|
65
77
|
end
|
66
78
|
|
79
|
+
def self.clear_caches!
|
80
|
+
clear_rules_modules!
|
81
|
+
clear_helper_modules!
|
82
|
+
BUILD_CACHE_CONFIG.each_value { |v| v[:cache].clear }
|
83
|
+
end
|
84
|
+
|
67
85
|
def self.rules_modules
|
68
86
|
@rules_modules ||= {
|
69
87
|
type: :object,
|
@@ -87,8 +105,8 @@ module LazyGraph
|
|
87
105
|
}
|
88
106
|
end
|
89
107
|
|
90
|
-
def self.eval!(modules:, context:, query:, debug: false, validate:
|
91
|
-
|
108
|
+
def self.eval!(modules:, context:, query:, debug: false, validate: true)
|
109
|
+
graph = cache_as(:graph, [modules, debug, validate]) do
|
92
110
|
invalid_modules = modules.reject { |k, _v| rules_modules[:properties].key?(k.to_sym) }
|
93
111
|
return format_error_response('Invalid Modules', invalid_modules.keys.join(',')) unless invalid_modules.empty?
|
94
112
|
|
@@ -99,21 +117,23 @@ module LazyGraph
|
|
99
117
|
builder = build_modules(modules)
|
100
118
|
return builder if builder.is_a?(Hash)
|
101
119
|
|
102
|
-
builder
|
120
|
+
builder.build!(debug: debug, validate: validate)
|
103
121
|
end
|
104
122
|
|
105
|
-
context_result = cache_as(:context, [
|
106
|
-
|
123
|
+
context_result = cache_as(:context, [graph, context]) do
|
124
|
+
build_context(graph, context)
|
107
125
|
end
|
108
126
|
|
109
|
-
return context_result if context_result.is_a?(Hash) && context_result[:type]
|
110
|
-
|
111
|
-
query_result = cache_as(:query, [context_result, query]){ context_result.query(*(query || '')) }
|
127
|
+
return context_result if context_result.is_a?(Hash) && context_result[:type].equal?(:error)
|
112
128
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
129
|
+
cache_as(:query, [context_result, query]) do
|
130
|
+
HashUtils.strip_missing(
|
131
|
+
{
|
132
|
+
type: :success,
|
133
|
+
result: context_result.resolve(*(query || ''))
|
134
|
+
}
|
135
|
+
)
|
136
|
+
end
|
117
137
|
rescue SystemStackError => e
|
118
138
|
LazyGraph.logger.error(e.message)
|
119
139
|
LazyGraph.logger.error(e.backtrace.join("\n"))
|
@@ -132,9 +152,12 @@ module LazyGraph
|
|
132
152
|
cache.delete(cache.keys.first) while cache.size > max_size
|
133
153
|
end
|
134
154
|
|
155
|
+
def to_str
|
156
|
+
"LazyGraph(modules=#{@path})"
|
157
|
+
end
|
158
|
+
|
135
159
|
private_class_method def self.method_missing(method_name, *args, &block) = new.send(method_name, *args, &block)
|
136
160
|
private_class_method def self.respond_to_missing?(_, _ = false) = true
|
137
|
-
|
138
161
|
private_class_method def self.validate_modules(input_options)
|
139
162
|
JSON::Validator.validate!(rules_modules, input_options)
|
140
163
|
''
|
@@ -156,15 +179,19 @@ module LazyGraph
|
|
156
179
|
acc.send(k, **v.to_h.transform_keys(&:to_sym))
|
157
180
|
end
|
158
181
|
rescue ArgumentError => e
|
182
|
+
LazyGraph.logger.error(e.message)
|
183
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
159
184
|
format_error_response('Invalid Module Argument', e.message)
|
160
|
-
LazyGraph.logger.error(e.backtrace)
|
161
185
|
end
|
162
186
|
|
163
|
-
private_class_method def self.
|
164
|
-
|
165
|
-
rescue
|
187
|
+
private_class_method def self.build_context(graph, context)
|
188
|
+
graph.context(context)
|
189
|
+
rescue StandardError => e
|
190
|
+
if graph.debug?
|
191
|
+
LazyGraph.logger.error(e.message)
|
192
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
193
|
+
end
|
166
194
|
format_error_response('Invalid Context Input', e.message)
|
167
|
-
LazyGraph.logger.error(e.backtrace)
|
168
195
|
end
|
169
196
|
end
|
170
197
|
end
|
@@ -2,8 +2,13 @@ module LazyGraph
|
|
2
2
|
class BuilderGroup
|
3
3
|
# A builder group is simply a named colleciton of builders (each a subclass of LazyGraph::Builder)
|
4
4
|
# That can be reloaded in bulk, and exposed via a simple HTTP server interface.
|
5
|
-
|
5
|
+
# If a script file defining a builder group is run directly, it will start a Puma server
|
6
|
+
# to host itself
|
7
|
+
def self.bootstrap!(reload_paths: nil, exclude_paths: [], listen: Environment.development?, enable_server: true)
|
8
|
+
bootstrap_script_name = caller.drop_while { |r| r =~ /builder_group/ }[0][/[^:]+/]
|
9
|
+
reload_paths ||= File.join(File.dirname(bootstrap_script_name), '**/*.rb')
|
6
10
|
reload_paths = Array(reload_paths)
|
11
|
+
|
7
12
|
Module.new do
|
8
13
|
define_singleton_method(:included) do |base|
|
9
14
|
def base.each_builder(const = self, &blk)
|
@@ -18,40 +23,48 @@ module LazyGraph
|
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
21
|
-
def base.
|
22
|
-
|
26
|
+
def base.rack_app(**opts)
|
27
|
+
unless defined?(Rack)
|
28
|
+
raise AbortError,
|
29
|
+
'You must install rack first to be able to run a LazyGraph BuilderGroup as a server'
|
30
|
+
end
|
31
|
+
|
32
|
+
LazyGraph::RackApp.new(
|
23
33
|
routes: each_builder.map do |builder|
|
24
34
|
[
|
25
|
-
builder.to_s.downcase.gsub('::', '/').gsub(/^#{name.downcase}/, ''),
|
35
|
+
builder.to_s.downcase.gsub('::', '/').gsub(/^#{name.to_s.downcase}/, ''),
|
26
36
|
builder
|
27
37
|
]
|
28
|
-
end.to_h
|
38
|
+
end.to_h,
|
39
|
+
**opts
|
29
40
|
)
|
30
41
|
end
|
31
42
|
|
32
|
-
base.define_singleton_method(:reload_lazy_graphs!) do
|
33
|
-
each_builder
|
34
|
-
builder.clear_rules_modules!
|
35
|
-
builder.clear_helper_modules!
|
36
|
-
end
|
43
|
+
base.define_singleton_method(:reload_lazy_graphs!) do |clear_previous: false|
|
44
|
+
each_builder(&:clear_caches!) if clear_previous
|
37
45
|
|
38
|
-
reload_paths.flat_map { |p|
|
39
|
-
|
46
|
+
reload_paths.flat_map { |p|
|
47
|
+
Dir[p]
|
48
|
+
}.-([bootstrap_script_name]).sort_by { |file| file.count(File::SEPARATOR) }.each do |file|
|
49
|
+
load file unless exclude_paths.any? { |p| p =~ file }
|
40
50
|
rescue StandardError => e
|
41
51
|
LazyGraph.logger.error("Failed to load #{file}: #{e.message}")
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
|
-
base.reload_lazy_graphs!
|
55
|
+
base.reload_lazy_graphs! if reload_paths.any?
|
46
56
|
|
47
|
-
|
57
|
+
if listen && defined?(Listen)
|
58
|
+
Listen.to(*reload_paths.map { |p| p.gsub(%r{(?:/\*\*)*/\*\.rb}, '') }) do
|
59
|
+
base.reload_lazy_graphs!(clear_previous: true)
|
60
|
+
end.start
|
61
|
+
end
|
48
62
|
|
49
|
-
|
50
|
-
Listen.to(*reload_paths.map { |p| p.gsub(%r{(?:/\*\*)*/\*\.rb}, '') }) do
|
51
|
-
base.reload_lazy_graphs!
|
52
|
-
end.start
|
63
|
+
CLI.invoke!(base.rack_app) if enable_server && bootstrap_script_name == $0
|
53
64
|
end
|
54
65
|
end
|
55
66
|
end
|
56
67
|
end
|
68
|
+
|
69
|
+
define_singleton_method(:bootstrap_app!, &BuilderGroup.method(:bootstrap!))
|
57
70
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LazyGraph
|
4
|
+
class CLI
|
5
|
+
# A builder group rack-app can be exposed via a simple rack based HTTP server
|
6
|
+
# with routes corresponding to each builder class deeply nested
|
7
|
+
# withhin the builder group module.
|
8
|
+
def self.invoke!(rack_app)
|
9
|
+
require 'etc'
|
10
|
+
require 'optparse'
|
11
|
+
require 'rack'
|
12
|
+
|
13
|
+
options = {
|
14
|
+
port: 9292,
|
15
|
+
workers: (Environment.development? ? 1 : Etc.nprocessors)
|
16
|
+
}
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: #{$0} [options]"
|
20
|
+
|
21
|
+
opts.on('-p PORT', '--port PORT', Integer, "Set the port (default: #{options[:port]})") do |p|
|
22
|
+
options[:port] = p
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('-w WORKERS', '--workers WORKERS', Integer,
|
26
|
+
"Set the number of workers (default: #{options[:workers]})") do |w|
|
27
|
+
options[:workers] = w
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-h', '--help', 'Print this help') do
|
31
|
+
puts opts
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
end.parse!
|
35
|
+
# A builder group can be exposed as a simple rack based HTTP server
|
36
|
+
# with routes corresponding to each builder class deeply nested
|
37
|
+
# withhin the builder group module.
|
38
|
+
|
39
|
+
RackServer.new(
|
40
|
+
Rack::Builder.new do
|
41
|
+
use Rack::Lint if Environment.development?
|
42
|
+
run rack_app
|
43
|
+
end
|
44
|
+
).start(port: options[:port], workers: options[:workers])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/lazy_graph/context.rb
CHANGED
@@ -11,17 +11,34 @@ module LazyGraph
|
|
11
11
|
graph.validate!(input) if [true, 'input'].include?(graph.validate)
|
12
12
|
@graph = graph
|
13
13
|
@input = input
|
14
|
+
@graph.root_node.properties.each_key do |key|
|
15
|
+
define_singleton_method(key) { get(key) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_json(path)
|
20
|
+
HashUtils.strip_missing(get(path))
|
21
|
+
end
|
22
|
+
|
23
|
+
def get(path)
|
24
|
+
result = resolve(path)
|
25
|
+
raise result[:error] if result[:err]
|
26
|
+
|
27
|
+
result[:output]
|
14
28
|
end
|
15
29
|
|
16
|
-
def
|
17
|
-
|
30
|
+
def debug(path)
|
31
|
+
result = resolve(path)
|
32
|
+
raise result[:error] if result[:err]
|
33
|
+
|
34
|
+
result[:debug_trace]
|
18
35
|
end
|
19
36
|
|
20
37
|
def resolve(path)
|
21
38
|
@input = @graph.root_node.fetch_item({ input: @input }, :input, nil)
|
22
39
|
|
23
|
-
query = PathParser.parse(path, true)
|
24
|
-
stack = StackPointer.new(nil, @input, 0, :'$', nil)
|
40
|
+
query = PathParser.parse(path, strip_root: true)
|
41
|
+
stack = StackPointer.new(nil, @input, 0, 0, :'$', nil)
|
25
42
|
stack.root = stack
|
26
43
|
|
27
44
|
result = @graph.root_node.resolve(query, stack)
|
@@ -32,18 +49,20 @@ module LazyGraph
|
|
32
49
|
stack.frame[:DEBUG] = nil
|
33
50
|
end
|
34
51
|
{
|
35
|
-
output:
|
36
|
-
debug_trace:
|
52
|
+
output: result,
|
53
|
+
debug_trace: debug_trace
|
37
54
|
}
|
38
|
-
rescue
|
55
|
+
rescue AbortError, ValidationError => e
|
39
56
|
{
|
40
|
-
output:
|
57
|
+
output: nil, err: e.message, status: :abort, error: e
|
41
58
|
}
|
42
59
|
rescue StandardError => e
|
43
|
-
|
44
|
-
|
60
|
+
if @graph.debug?
|
61
|
+
LazyGraph.logger.error(e.message)
|
62
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
63
|
+
end
|
45
64
|
{
|
46
|
-
output:
|
65
|
+
output: nil, err: e.message, backtrace: e.backtrace, error: e
|
47
66
|
}
|
48
67
|
end
|
49
68
|
|
@@ -54,9 +73,15 @@ module LazyGraph
|
|
54
73
|
q.text 'graph='
|
55
74
|
q.pp(@graph)
|
56
75
|
end
|
76
|
+
q.group do
|
77
|
+
q.text 'input='
|
78
|
+
q.pp(@input)
|
79
|
+
end
|
57
80
|
end
|
58
81
|
end
|
59
82
|
|
60
|
-
|
83
|
+
def [](*parts)
|
84
|
+
get(parts.map { |p| p.is_a?(Integer) ? "[#{p}]" : p.to_s }.join('.').gsub('.[', '['))
|
85
|
+
end
|
61
86
|
end
|
62
87
|
end
|