lazy_graph 0.1.1 → 0.1.2
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/examples/performance_tests.rb +3 -3
- data/lib/lazy_graph/builder/dsl.rb +10 -13
- data/lib/lazy_graph/builder.rb +35 -8
- data/lib/lazy_graph/context.rb +6 -5
- data/lib/lazy_graph/graph.rb +22 -32
- data/lib/lazy_graph/hash_utils.rb +30 -35
- data/lib/lazy_graph/missing_value.rb +2 -0
- data/lib/lazy_graph/node/array_node.rb +9 -4
- data/lib/lazy_graph/node/derived_rules.rb +14 -7
- data/lib/lazy_graph/node/node_properties.rb +9 -12
- data/lib/lazy_graph/node/object_node.rb +25 -24
- data/lib/lazy_graph/node/symbol_hash.rb +26 -0
- data/lib/lazy_graph/node.rb +63 -57
- data/lib/lazy_graph/path_parser/path.rb +2 -2
- data/lib/lazy_graph/server.rb +7 -2
- data/lib/lazy_graph/stack_pointer.rb +20 -10
- data/lib/lazy_graph/version.rb +1 -1
- metadata +4 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29043baea3639e83c226d8e157af818466d50d4e3c125853dac64ae411aa0c0a
|
4
|
+
data.tar.gz: f137caa57317cd9046bc16384bf657ca7c43d10b12701f30be6bc7a31580c6db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1c5107a005882da016caa1fd54afa5131f296637fd6a7b18810d3b2c1f18893ae11938e63fc42ceec05def4ceba76afbe016b152d1c107225af148b06f6db52
|
7
|
+
data.tar.gz: b5a2126ac97b38e1e0d4ce23a1d46859a89979b0b09a2d3ad3e3676b763d6277745310ee08a0c49fbbc85bcd930d246362bd79793899726c2f763b0aa1f6fe44
|
@@ -15,14 +15,14 @@ class PerformanceBuilder < LazyGraph::Builder
|
|
15
15
|
|
16
16
|
object :position, rule: :"${$.positions[position_id]}"
|
17
17
|
object :pay_schedule, rule: :'${pay_schedules[pay_schedule_id]}'
|
18
|
-
number :
|
18
|
+
number :pay_rate, rule: :"${position.pay_rate}"
|
19
19
|
string :employee_id, rule: :id
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
23
|
object :positions do
|
24
24
|
object :".*", pattern_property: true do
|
25
|
-
number :
|
25
|
+
number :pay_rate
|
26
26
|
number :salary, default: 100_000
|
27
27
|
end
|
28
28
|
end
|
@@ -56,7 +56,7 @@ def gen_employees(n, m = 10)
|
|
56
56
|
end.to_h,
|
57
57
|
positions: [*1..m].map do |i|
|
58
58
|
[i, {
|
59
|
-
|
59
|
+
pay_rate: Random.rand(10..100)
|
60
60
|
}]
|
61
61
|
end.to_h
|
62
62
|
}
|
@@ -39,7 +39,7 @@ module LazyGraph
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def set_pattern_property(pattern, value)
|
42
|
-
pattern = pattern.
|
42
|
+
pattern = pattern.to_sym
|
43
43
|
properties = schema[:patternProperties] ||= {}
|
44
44
|
properties[pattern] = \
|
45
45
|
if properties.key?(pattern) && %i[object array].include?(properties[pattern][:type])
|
@@ -261,16 +261,12 @@ module LazyGraph
|
|
261
261
|
**(description ? { description: description } : {}),
|
262
262
|
**(rule ? { rule: rule } : {}),
|
263
263
|
**opts,
|
264
|
-
items: {
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
}
|
264
|
+
items: { properties: {} }.tap do |items|
|
265
|
+
yields(items) do
|
266
|
+
send(type, :items, &blk)
|
267
|
+
end
|
268
|
+
end[:properties][:items]
|
270
269
|
}
|
271
|
-
yields(new_array) do
|
272
|
-
items(&blk)
|
273
|
-
end
|
274
270
|
required(name) if required && default.nil? && rule.nil?
|
275
271
|
pattern_property ? set_pattern_property(name, new_array) : set_property(name, new_array)
|
276
272
|
end
|
@@ -291,18 +287,19 @@ module LazyGraph
|
|
291
287
|
def rule_from_when(when_clause)
|
292
288
|
inputs = when_clause.keys
|
293
289
|
conditions = when_clause
|
294
|
-
|
290
|
+
calc = "{#{when_clause.keys.map { |k| "#{k}: #{k}}" }.join(', ')}"
|
295
291
|
{
|
296
292
|
inputs: inputs,
|
297
293
|
conditions: conditions,
|
298
|
-
|
294
|
+
fixed_result: when_clause,
|
295
|
+
calc: calc
|
299
296
|
}
|
300
297
|
end
|
301
298
|
|
302
299
|
def rule_from_first_of(prop_list)
|
303
300
|
{
|
304
301
|
inputs: prop_list,
|
305
|
-
|
302
|
+
calc: "itself.get_first_of(:#{prop_list.join(', :')})"
|
306
303
|
}
|
307
304
|
end
|
308
305
|
|
data/lib/lazy_graph/builder.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
# Subclass LazyGraph::Builder to create new builder classes
|
4
3
|
# which can be used to easily build a rule-set to be used as a LazyGraph.
|
5
4
|
#
|
@@ -7,6 +6,17 @@ require_relative 'builder/dsl'
|
|
7
6
|
|
8
7
|
module LazyGraph
|
9
8
|
class Builder
|
9
|
+
|
10
|
+
# Cache up to a fixed number of graphs, context and queries
|
11
|
+
BUILD_CACHE_CONFIG = {
|
12
|
+
# Store up to 1000 graphs
|
13
|
+
graph: {size: 1000, cache: {}},
|
14
|
+
# Store up to 5000 configs
|
15
|
+
context: {size: 5000, cache: {}},
|
16
|
+
# Store up to 5000 queries
|
17
|
+
query: {size: 5000, cache: {}}
|
18
|
+
}.compare_by_identity.freeze
|
19
|
+
|
10
20
|
include DSL
|
11
21
|
# This class is responsible for piece-wise building of rules,
|
12
22
|
# as a combined schema definition.
|
@@ -29,7 +39,13 @@ module LazyGraph
|
|
29
39
|
|
30
40
|
# Helper for defining a new entity in the schema (just a shorthand for defining a new method for now)
|
31
41
|
def self.entity(name, &blk)
|
32
|
-
|
42
|
+
module_body_func_name = :"_#{name}"
|
43
|
+
define_method(module_body_func_name, &blk)
|
44
|
+
define_method(name) do |**args, &inner_blk|
|
45
|
+
send(module_body_func_name, **args)
|
46
|
+
inner_blk&.call
|
47
|
+
self
|
48
|
+
end
|
33
49
|
end
|
34
50
|
|
35
51
|
class << self
|
@@ -72,7 +88,7 @@ module LazyGraph
|
|
72
88
|
end
|
73
89
|
|
74
90
|
def self.eval!(modules:, context:, query:, debug: false, validate: false)
|
75
|
-
|
91
|
+
builder = cache_as(:graph, [modules, debug, validate].hash) do
|
76
92
|
invalid_modules = modules.reject { |k, _v| rules_modules[:properties].key?(k.to_sym) }
|
77
93
|
return format_error_response('Invalid Modules', invalid_modules.keys.join(',')) unless invalid_modules.empty?
|
78
94
|
|
@@ -83,21 +99,24 @@ module LazyGraph
|
|
83
99
|
builder = build_modules(modules)
|
84
100
|
return builder if builder.is_a?(Hash)
|
85
101
|
|
86
|
-
|
102
|
+
builder
|
87
103
|
end
|
88
104
|
|
89
|
-
|
105
|
+
context_result = cache_as(:context, [builder, context]) do
|
106
|
+
evaluate_context(builder, context, debug: debug, validate: validate)
|
107
|
+
end
|
90
108
|
|
91
109
|
return context_result if context_result.is_a?(Hash) && context_result[:type] == :error
|
92
110
|
|
111
|
+
query_result = cache_as(:query, [context_result, query]){ context_result.query(*(query || '')) }
|
112
|
+
|
93
113
|
{
|
94
114
|
type: :success,
|
95
|
-
result:
|
115
|
+
result: query_result
|
96
116
|
}
|
97
|
-
|
98
117
|
rescue SystemStackError => e
|
99
118
|
LazyGraph.logger.error(e.message)
|
100
|
-
LazyGraph.logger.error(e.backtrace)
|
119
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
101
120
|
{
|
102
121
|
type: :error,
|
103
122
|
message: 'Recursive Query Detected',
|
@@ -105,6 +124,14 @@ module LazyGraph
|
|
105
124
|
}
|
106
125
|
end
|
107
126
|
|
127
|
+
def self.cache_as(type, key)
|
128
|
+
cache, max_size = BUILD_CACHE_CONFIG[type].values_at(:cache, :size)
|
129
|
+
key = key.hash
|
130
|
+
cache[key] = cache[key] ? cache.delete(key) : yield
|
131
|
+
ensure
|
132
|
+
cache.delete(cache.keys.first) while cache.size > max_size
|
133
|
+
end
|
134
|
+
|
108
135
|
private_class_method def self.method_missing(method_name, *args, &block) = new.send(method_name, *args, &block)
|
109
136
|
private_class_method def self.respond_to_missing?(_, _ = false) = true
|
110
137
|
|
data/lib/lazy_graph/context.rb
CHANGED
@@ -7,24 +7,25 @@ module LazyGraph
|
|
7
7
|
attr_accessor :ruleset, :input
|
8
8
|
|
9
9
|
def initialize(graph, input)
|
10
|
-
input = HashUtils.deep_dup(input)
|
11
|
-
HashUtils.deep_symbolize!(input)
|
10
|
+
input = HashUtils.deep_dup(input, symbolize: true)
|
12
11
|
graph.validate!(input) if [true, 'input'].include?(graph.validate)
|
13
12
|
@graph = graph
|
14
13
|
@input = input
|
15
14
|
end
|
16
15
|
|
17
16
|
def query(paths)
|
18
|
-
paths.is_a?(Array) ? paths.map { |path| resolve(
|
17
|
+
paths.is_a?(Array) ? paths.map { |path| resolve(path) } : resolve(paths)
|
19
18
|
end
|
20
19
|
|
21
|
-
def resolve(
|
22
|
-
@input = @graph.root_node.fetch_item({ input: input }, :input, nil)
|
20
|
+
def resolve(path)
|
21
|
+
@input = @graph.root_node.fetch_item({ input: @input }, :input, nil)
|
22
|
+
|
23
23
|
query = PathParser.parse(path, true)
|
24
24
|
stack = StackPointer.new(nil, @input, 0, :'$', nil)
|
25
25
|
stack.root = stack
|
26
26
|
|
27
27
|
result = @graph.root_node.resolve(query, stack)
|
28
|
+
|
28
29
|
@graph.root_node.clear_visits!
|
29
30
|
if @graph.debug?
|
30
31
|
debug_trace = stack.frame[:DEBUG]
|
data/lib/lazy_graph/graph.rb
CHANGED
@@ -13,18 +13,13 @@ module LazyGraph
|
|
13
13
|
def context(input) = Context.new(self, input)
|
14
14
|
def debug? = @debug
|
15
15
|
|
16
|
-
def initialize(
|
17
|
-
@json_schema = HashUtils.deep_dup(
|
18
|
-
|
16
|
+
def initialize(input_schema, debug: false, validate: true, helpers: nil)
|
17
|
+
@json_schema = HashUtils.deep_dup(input_schema, symbolize: true, signature: signature = [0]).merge(type: :object)
|
19
18
|
@debug = debug
|
20
19
|
@validate = validate
|
21
20
|
@helpers = helpers
|
22
21
|
|
23
|
-
signature
|
24
|
-
if [true, 'schema'].include?(validate)
|
25
|
-
VALIDATION_CACHE[signature] ||= validate!(@json_schema, METASCHEMA)
|
26
|
-
true
|
27
|
-
end
|
22
|
+
VALIDATION_CACHE[signature[0]] ||= validate!(@json_schema, METASCHEMA) if [true, 'schema'].include?(validate)
|
28
23
|
|
29
24
|
if @json_schema[:type].to_sym != :object || @json_schema[:properties].nil?
|
30
25
|
raise ArgumentError, 'Root schema must be a non-empty object'
|
@@ -35,31 +30,26 @@ module LazyGraph
|
|
35
30
|
|
36
31
|
def build_node(schema, path = :'$', name = :root, parent = nil)
|
37
32
|
schema[:type] = schema[:type].to_sym
|
38
|
-
case schema[:type]
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
33
|
+
node = case schema[:type]
|
34
|
+
when :object then ObjectNode
|
35
|
+
when :array then ArrayNode
|
36
|
+
else Node
|
37
|
+
end.new(name, path, schema, parent, debug: @debug, helpers: @helpers)
|
38
|
+
|
39
|
+
if node.type == :object
|
40
|
+
node.children = \
|
41
|
+
{
|
42
|
+
properties: schema.fetch(:properties, {}).map do |key, value|
|
43
|
+
[key, build_node(value, :"#{path}.#{key}", key, node)]
|
44
|
+
end.to_h.compare_by_identity,
|
45
|
+
pattern_properties: schema.fetch(:patternProperties, {}).map do |key, value|
|
46
|
+
[Regexp.new(key.to_s), build_node(value, :"#{path}.#{key}", :'<property>', node)]
|
47
|
+
end
|
48
|
+
}
|
49
|
+
elsif node.type == :array
|
50
|
+
node.children = build_node(schema.fetch(:items, {}), :"#{path}[]", :items, node)
|
44
51
|
end
|
45
|
-
|
46
|
-
|
47
|
-
def build_children(node, schema, path)
|
48
|
-
case node.type
|
49
|
-
when :object then build_object_children(schema, path, node)
|
50
|
-
when :array then build_node(schema.fetch(:items, {}), :"#{path}[]", :items, node)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def build_object_children(schema, path, parent)
|
55
|
-
{
|
56
|
-
properties: schema.fetch(:properties, {}).map do |key, value|
|
57
|
-
[key, build_node(value, "#{path}.#{key}", key, parent)]
|
58
|
-
end.to_h.compare_by_identity,
|
59
|
-
pattern_properties: schema.fetch(:patternProperties, {}).map do |key, value|
|
60
|
-
[Regexp.new(key.to_s), build_node(value, :"#{path}.#{key}", :'<property>', parent)]
|
61
|
-
end.to_h
|
62
|
-
}
|
52
|
+
node
|
63
53
|
end
|
64
54
|
|
65
55
|
def validate!(input, schema = @json_schema)
|
@@ -4,25 +4,43 @@ module LazyGraph
|
|
4
4
|
module HashUtils
|
5
5
|
module_function
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
# Deeply duplicates a nested hash or array, preserving object identity.
|
8
|
+
# Optionally symbolizes keys on the way, and/or generates a signature.
|
9
|
+
def deep_dup(obj, symbolize: false, signature: nil)
|
10
|
+
case obj
|
9
11
|
when Hash
|
10
|
-
|
12
|
+
obj.each_with_object(symbolize ? {}.compare_by_identity : {}) do |(key, value), result|
|
13
|
+
key = \
|
14
|
+
if !symbolize || key.is_a?(Symbol)
|
15
|
+
key
|
16
|
+
else
|
17
|
+
key.is_a?(String) ? key.to_sym : key.to_s.to_sym
|
18
|
+
end
|
19
|
+
|
20
|
+
signature[0] ^= key.object_id if signature
|
21
|
+
result[key] = deep_dup(value, symbolize: symbolize, signature: signature)
|
22
|
+
end
|
11
23
|
when Array
|
12
|
-
|
24
|
+
obj.map { |value| deep_dup(value, symbolize: symbolize, signature: signature) }
|
25
|
+
when String, Numeric, TrueClass, FalseClass, NilClass
|
26
|
+
signature[0] ^= obj.hash if signature
|
27
|
+
obj
|
28
|
+
else
|
29
|
+
obj
|
13
30
|
end
|
14
|
-
hash
|
15
31
|
end
|
16
32
|
|
17
|
-
def deep_merge(hash, other_hash, path =
|
33
|
+
def deep_merge(hash, other_hash, path = '')
|
18
34
|
hash.merge(other_hash.transform_keys(&:to_sym)) do |key, this_val, other_val|
|
35
|
+
current_path = path.empty? ? key.to_s : "#{path}.#{key}"
|
36
|
+
|
19
37
|
if this_val.is_a?(Hash) && other_val.is_a?(Hash) && other_val != this_val
|
20
|
-
deep_merge(this_val, other_val,
|
38
|
+
deep_merge(this_val, other_val, current_path)
|
21
39
|
elsif this_val.is_a?(Array) && other_val.is_a?(Array) && other_val != this_val
|
22
|
-
this_val
|
40
|
+
(this_val | other_val)
|
23
41
|
else
|
24
42
|
if this_val != other_val && !(this_val.is_a?(Proc) && other_val.is_a?(Proc))
|
25
|
-
LazyGraph.logger.warn("
|
43
|
+
LazyGraph.logger.warn("Conflicting values at #{current_path}: #{this_val.inspect} != #{other_val.inspect}")
|
26
44
|
end
|
27
45
|
other_val
|
28
46
|
end
|
@@ -48,37 +66,14 @@ module LazyGraph
|
|
48
66
|
res[key] = strip_invalid(obj[key], parent_list)
|
49
67
|
end
|
50
68
|
when Array
|
51
|
-
obj.map
|
69
|
+
obj.map { |value| strip_invalid(value, parent_list) }
|
70
|
+
when MissingValue
|
71
|
+
nil
|
52
72
|
else
|
53
73
|
obj
|
54
74
|
end
|
55
75
|
ensure
|
56
76
|
parent_list.delete(obj) unless circular_dependency
|
57
77
|
end
|
58
|
-
|
59
|
-
def deep_symbolize!(obj)
|
60
|
-
case obj
|
61
|
-
when Hash
|
62
|
-
hash = 0
|
63
|
-
obj.to_a.each do |key, value|
|
64
|
-
hash ^= deep_symbolize!(value)
|
65
|
-
unless key.is_a?(Symbol)
|
66
|
-
key.to_s.to_sym
|
67
|
-
obj[key.to_s.to_sym] = obj.delete(key)
|
68
|
-
end
|
69
|
-
hash ^= key.object_id
|
70
|
-
end
|
71
|
-
obj.compare_by_identity
|
72
|
-
hash
|
73
|
-
when Array
|
74
|
-
hash = 0
|
75
|
-
obj.each { |item| hash ^= deep_symbolize!(item) }
|
76
|
-
hash
|
77
|
-
when String, Numeric, TrueClass, FalseClass, NilClass
|
78
|
-
obj.hash
|
79
|
-
else
|
80
|
-
0
|
81
|
-
end
|
82
|
-
end
|
83
78
|
end
|
84
79
|
end
|
@@ -15,9 +15,9 @@ module LazyGraph
|
|
15
15
|
**
|
16
16
|
)
|
17
17
|
input = stack_memory.frame
|
18
|
-
@visited[input.object_id ^ path.shifted_id] ||= begin
|
18
|
+
@visited[input.object_id >> 2 ^ path.shifted_id] ||= begin
|
19
19
|
if (path_segment = path.segment).is_a?(PathParser::PathGroup)
|
20
|
-
unless path_segment.
|
20
|
+
unless path_segment.index?
|
21
21
|
return input.length.times.map do |index|
|
22
22
|
item = children.fetch_item(input, index, stack_memory)
|
23
23
|
children.resolve(path, stack_memory.push(item, index))
|
@@ -46,7 +46,7 @@ module LazyGraph
|
|
46
46
|
@children.resolve(path.next, stack_memory.push(item, index))
|
47
47
|
end
|
48
48
|
else
|
49
|
-
if @
|
49
|
+
if @child_properties&.key?(segment) || input&.first&.key?(segment)
|
50
50
|
input.length.times.map do |index|
|
51
51
|
item = children.fetch_item(input, index, stack_memory)
|
52
52
|
@children.resolve(path, stack_memory.push(item, index))
|
@@ -60,8 +60,13 @@ module LazyGraph
|
|
60
60
|
should_recycle&.recycle!
|
61
61
|
end
|
62
62
|
|
63
|
+
def children=(value)
|
64
|
+
@children = value
|
65
|
+
@child_properties = @children.children[:properties].compare_by_identity if @children.is_object
|
66
|
+
end
|
67
|
+
|
63
68
|
def cast(value)
|
64
|
-
value
|
69
|
+
value
|
65
70
|
end
|
66
71
|
end
|
67
72
|
end
|
@@ -29,6 +29,7 @@ module LazyGraph
|
|
29
29
|
derived = interpret_derived_proc(derived) if derived.is_a?(Proc)
|
30
30
|
derived = { inputs: derived.to_s } if derived.is_a?(String) || derived.is_a?(Symbol)
|
31
31
|
derived[:inputs] = parse_derived_inputs(derived)
|
32
|
+
@fixed_result = derived[:fixed_result]
|
32
33
|
@copy_input = true if !derived[:calc] && derived[:inputs].size == 1
|
33
34
|
extract_derived_src(derived) if @debug
|
34
35
|
|
@@ -42,7 +43,7 @@ module LazyGraph
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def interpret_derived_proc(derived)
|
45
|
-
src, requireds, optionals, keywords, = DerivedRules.extract_expr_from_source_location(derived.source_location)
|
46
|
+
src, requireds, optionals, keywords, proc_line, = DerivedRules.extract_expr_from_source_location(derived.source_location)
|
46
47
|
src = src.body&.slice || ''
|
47
48
|
@src = src.lines.map(&:strip)
|
48
49
|
inputs, conditions = parse_args_with_conditions(requireds, optionals, keywords)
|
@@ -56,7 +57,7 @@ module LazyGraph
|
|
56
57
|
# rubocop:disable:next-line
|
57
58
|
derived.source_location.first,
|
58
59
|
# rubocop:enable
|
59
|
-
derived.source_location.last.succ
|
60
|
+
derived.source_location.last.succ.succ
|
60
61
|
)
|
61
62
|
}
|
62
63
|
end
|
@@ -116,7 +117,7 @@ module LazyGraph
|
|
116
117
|
keywords = (src.parameters&.parameters&.keywords || []).map do |kw|
|
117
118
|
[kw.name, kw.value.slice.gsub(/^_\./, '$.')]
|
118
119
|
end.to_h
|
119
|
-
[src, requireds, optionals, keywords, mtime]
|
120
|
+
[src, requireds, optionals, keywords, proc_line, mtime]
|
120
121
|
end
|
121
122
|
end
|
122
123
|
|
@@ -142,10 +143,11 @@ module LazyGraph
|
|
142
143
|
end
|
143
144
|
input_hash.invert
|
144
145
|
else
|
145
|
-
{ inputs.
|
146
|
+
{ inputs.to_s.gsub(/[^(?:[A-Za-z][A-Za-z0-9_])]/, '__') => inputs.to_s.freeze }
|
146
147
|
end
|
147
148
|
when Array
|
148
|
-
|
149
|
+
pairs = inputs.last.is_a?(Hash) ? inputs.pop : {}
|
150
|
+
inputs.map { |v| { v.to_s.gsub(/[^(?:[A-Za-z][A-Za-z0-9_])]/, '__') => v } }.reduce(pairs, :merge)
|
149
151
|
when Hash
|
150
152
|
inputs
|
151
153
|
else
|
@@ -164,7 +166,11 @@ module LazyGraph
|
|
164
166
|
end
|
165
167
|
|
166
168
|
def parse_rule_string(derived)
|
167
|
-
|
169
|
+
calc_str = derived[:calc]
|
170
|
+
src = @src
|
171
|
+
instance_eval(
|
172
|
+
"->{ begin; #{calc_str}; rescue StandardError => e; LazyGraph.logger.error(\"Exception in \#{src}. \#{e.message}\"); LazyGraph.logger.error(e.backtrace.join(\"\\n\")); raise; end }", __FILE__, __LINE__
|
173
|
+
)
|
168
174
|
rescue SyntaxError
|
169
175
|
missing_value = MissingValue { "Syntax error in #{derived[:src]}" }
|
170
176
|
-> { missing_value }
|
@@ -176,7 +182,8 @@ module LazyGraph
|
|
176
182
|
Struct.new(*(derived[:inputs].keys.map(&:to_sym) + %i[itself stack_ptr])) do
|
177
183
|
def missing?(value) = value.is_a?(LazyGraph::MissingValue) || value.nil?
|
178
184
|
helpers&.each { |h| include h }
|
179
|
-
|
185
|
+
|
186
|
+
define_method(:process!, &derived[:calc]) if derived[:calc].is_a?(Proc)
|
180
187
|
def method_missing(name, *args, &block)
|
181
188
|
stack_ptr.send(name, *args, &block)
|
182
189
|
end
|
@@ -5,13 +5,11 @@ module LazyGraph
|
|
5
5
|
def self.build(members:, invisible:)
|
6
6
|
Struct.new(*members, keyword_init: true) do
|
7
7
|
define_method(:initialize) do |kws|
|
8
|
-
members.each { |k| self[k] = kws[k]
|
8
|
+
members.each { |k| self[k] = kws[k].then { |v| v.nil? ? MissingValue::BLANK : v } }
|
9
9
|
end
|
10
10
|
|
11
11
|
define_method(:key?) do |x|
|
12
12
|
!self[x].equal?(MissingValue::BLANK)
|
13
|
-
rescue StandardError
|
14
|
-
nil
|
15
13
|
end
|
16
14
|
|
17
15
|
define_method(:[]=) do |key, val|
|
@@ -26,12 +24,16 @@ module LazyGraph
|
|
26
24
|
invisible
|
27
25
|
end
|
28
26
|
|
27
|
+
def to_hash
|
28
|
+
to_h
|
29
|
+
end
|
30
|
+
|
29
31
|
define_method(:each_key, &members.method(:each))
|
30
32
|
|
31
33
|
def dup
|
32
|
-
self.class.new
|
33
|
-
|
34
|
-
|
34
|
+
duplicate = self.class.new
|
35
|
+
members.each { duplicate[_1] = self[_1].dup }
|
36
|
+
duplicate
|
35
37
|
end
|
36
38
|
|
37
39
|
def get_first_of(*props)
|
@@ -42,18 +44,13 @@ module LazyGraph
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def pretty_print(q)
|
45
|
-
# Start the custom pretty print
|
46
47
|
q.group(1, '<Props ', '>') do
|
47
48
|
q.seplist(members.zip(values).reject do |m, v|
|
48
49
|
m == :DEBUG && (v.nil? || v.is_a?(MissingValue))
|
49
50
|
end) do |member, value|
|
50
51
|
q.group do
|
51
52
|
q.text "#{member}="
|
52
|
-
|
53
|
-
q.pp(value) # Delegate to the nested value's pretty_print
|
54
|
-
else
|
55
|
-
q.text value.inspect
|
56
|
-
end
|
53
|
+
value.respond_to?(:pretty_print) ? q.pp(value) : q.text(value.inspect)
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module LazyGraph
|
2
2
|
class ObjectNode < Node
|
3
|
+
require_relative 'symbol_hash'
|
4
|
+
|
3
5
|
# An object supports the following types of path resolutions.
|
4
6
|
# 1. Property name: obj.property => value
|
5
7
|
# 2. Property name group: obj[property1, property2] => { property1: value1, property2: value2 }
|
@@ -11,7 +13,10 @@ module LazyGraph
|
|
11
13
|
preserve_keys: false
|
12
14
|
)
|
13
15
|
input = stack_memory.frame
|
14
|
-
|
16
|
+
|
17
|
+
@visited[input.object_id >> 2 ^ path.shifted_id] ||= begin
|
18
|
+
return input if input.is_a?(MissingValue)
|
19
|
+
|
15
20
|
if (path_segment = path.segment).is_a?(PathParser::PathGroup)
|
16
21
|
return path_segment.options.each_with_object({}.tap(&:compare_by_identity)) do |part, object|
|
17
22
|
resolve(part.merge(path.next), stack_memory, nil, preserve_keys: object)
|
@@ -23,9 +28,9 @@ module LazyGraph
|
|
23
28
|
item = node.fetch_item(input, key, stack_memory)
|
24
29
|
node.resolve(path.next, stack_memory.push(item, key))
|
25
30
|
end
|
26
|
-
if @
|
31
|
+
if @pattern_properties.any? && input.keys.length > @properties_a.length
|
27
32
|
input.each_key do |key|
|
28
|
-
node = !@properties[key] && @
|
33
|
+
node = !@properties[key] && @pattern_properties.find { |(pattern, _value)| pattern.match?(key) }&.last
|
29
34
|
item = node.fetch_item(input, key, stack_memory)
|
30
35
|
node.resolve(path.next, stack_memory.push(item, key))
|
31
36
|
end
|
@@ -40,14 +45,14 @@ module LazyGraph
|
|
40
45
|
elsif segment == :*
|
41
46
|
# rubocop:disable
|
42
47
|
(input.keys | @properties_a.map(&:first)).each do |key|
|
43
|
-
next unless (node = @properties[key] || @
|
48
|
+
next unless (node = @properties[key] || @pattern_properties.find do |(pattern, _value)|
|
44
49
|
pattern.match?(key)
|
45
50
|
end&.last)
|
46
51
|
|
47
52
|
item = node.fetch_item(input, key, stack_memory)
|
48
53
|
preserve_keys[key] = node.resolve(path.next, stack_memory.push(item, key))
|
49
54
|
end
|
50
|
-
elsif (_, prop = @
|
55
|
+
elsif (_, prop = @pattern_properties.find { |(key, _val)| key.match?(segment) })
|
51
56
|
item = prop.fetch_item(input, segment, stack_memory)
|
52
57
|
value = prop.resolve(
|
53
58
|
path.next, stack_memory.push(item, segment)
|
@@ -85,33 +90,29 @@ module LazyGraph
|
|
85
90
|
|
86
91
|
@properties = @children.fetch(:properties, {})
|
87
92
|
@properties.compare_by_identity
|
88
|
-
@pattern_properties = @children.fetch(:pattern_properties,
|
93
|
+
@pattern_properties = @children.fetch(:pattern_properties, [])
|
89
94
|
|
90
95
|
@properties_a = @properties.to_a
|
91
|
-
@pattern_properties_a = @pattern_properties.to_a
|
92
96
|
|
93
97
|
@has_properties = @properties.any? || @pattern_properties.any?
|
94
|
-
return if @pattern_properties.any?
|
95
|
-
return unless @properties.any?
|
96
98
|
|
97
|
-
|
98
|
-
|
99
|
-
|
99
|
+
return unless @properties.any? || @pattern_properties.any?
|
100
|
+
|
101
|
+
if @pattern_properties.any?
|
102
|
+
@property_class = SymbolHash
|
103
|
+
else
|
104
|
+
invisible = @properties.select { |_k, v| v.invisible }.map(&:first)
|
105
|
+
@property_class = PROPERTY_CLASSES[{ members: @properties.keys + (@debug && !parent ? [:DEBUG] : []),
|
106
|
+
invisible: invisible }]
|
107
|
+
end
|
108
|
+
define_singleton_method(:cast, build_caster)
|
100
109
|
end
|
101
110
|
|
102
|
-
def
|
103
|
-
if
|
104
|
-
value.
|
105
|
-
super(!k.is_a?(Symbol) ? k.to_s.to_sym : k, v)
|
106
|
-
end
|
107
|
-
value.define_singleton_method(:[]) do |k|
|
108
|
-
super(!k.is_a?(Symbol) ? k.to_s.to_sym : k)
|
109
|
-
end
|
110
|
-
value.compare_by_identity
|
111
|
-
elsif @property_class && !value.is_a?(@property_class)
|
112
|
-
@property_class.new(value.to_h)
|
111
|
+
private def build_caster
|
112
|
+
if @property_class
|
113
|
+
->(value) { value.is_a?(@property_class) ? value : @property_class.new(value.to_h) }
|
113
114
|
else
|
114
|
-
value
|
115
|
+
->(value) { value }
|
115
116
|
end
|
116
117
|
end
|
117
118
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module LazyGraph
|
2
|
+
class ObjectNode < Node
|
3
|
+
class SymbolHash < ::Hash
|
4
|
+
def initialize(input_hash)
|
5
|
+
super
|
6
|
+
merge!(input_hash)
|
7
|
+
end
|
8
|
+
|
9
|
+
def []=(key, value)
|
10
|
+
case key
|
11
|
+
when Symbol then super(key, value)
|
12
|
+
when String then super(key.to_sym, value)
|
13
|
+
else super(key.to_s.to_sym, value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def [](key)
|
18
|
+
case key
|
19
|
+
when Symbol then super(key)
|
20
|
+
when String then super(key.to_sym)
|
21
|
+
else super(key.to_s.to_sym)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/lazy_graph/node.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'debug'
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'bigdecimal/util'
|
@@ -48,16 +49,16 @@ module LazyGraph
|
|
48
49
|
@depth = parent ? parent.depth + 1 : 0
|
49
50
|
@root = parent ? parent.root : self
|
50
51
|
@type = node[:type]
|
51
|
-
@invisible = node[:invisible]
|
52
|
+
@invisible = debug ? false : node[:invisible]
|
52
53
|
@visited = {}.compare_by_identity
|
53
|
-
|
54
|
+
instance_variable_set("@is_#{@type}", true)
|
55
|
+
define_singleton_method(:cast, build_caster)
|
54
56
|
define_missing_value_proc!
|
55
57
|
|
56
58
|
@has_default = node.key?(:default)
|
57
|
-
@default = @has_default ? node[:default] : MissingValue { @name }
|
59
|
+
@default = @has_default ? cast(node[:default]) : MissingValue { @name }
|
58
60
|
@resolution_stack = []
|
59
61
|
|
60
|
-
instance_variable_set("@is_#{@type}", true)
|
61
62
|
build_derived_inputs(node[:rule], helpers) if node[:rule]
|
62
63
|
end
|
63
64
|
|
@@ -68,16 +69,46 @@ module LazyGraph
|
|
68
69
|
)
|
69
70
|
end
|
70
71
|
|
72
|
+
private def build_caster
|
73
|
+
if @is_decimal
|
74
|
+
->(value) { value.is_a?(BigDecimal) ? value : value.to_d }
|
75
|
+
elsif @is_date
|
76
|
+
->(value) { value.is_a?(String) ? Date.parse(value) : value }
|
77
|
+
elsif @is_boolean
|
78
|
+
lambda do |value|
|
79
|
+
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
80
|
+
value
|
81
|
+
else
|
82
|
+
value.is_a?(MissingValue) ? false : !!value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
elsif @is_timestamp
|
86
|
+
lambda do |value|
|
87
|
+
case value
|
88
|
+
when String
|
89
|
+
DateTime.parse(value).to_time
|
90
|
+
when Numeric
|
91
|
+
Time.at(value)
|
92
|
+
else
|
93
|
+
value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
else
|
97
|
+
->(value) { value }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
71
101
|
def clear_visits!
|
72
102
|
@visited.clear
|
103
|
+
@resolution_stack.clear
|
104
|
+
@path_cache = {}.clear
|
105
|
+
@resolvers = {}.clear
|
106
|
+
|
73
107
|
return unless @children
|
74
108
|
return @children.clear_visits! if @children.is_a?(Node)
|
75
109
|
|
76
|
-
@children[:properties]&.
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
@children[:pattern_properties]&.each do |_, node|
|
110
|
+
@children[:properties]&.each_value(&:clear_visits!)
|
111
|
+
@children[:pattern_properties]&.each do |(_, node)|
|
81
112
|
node.clear_visits!
|
82
113
|
end
|
83
114
|
end
|
@@ -101,7 +132,7 @@ module LazyGraph
|
|
101
132
|
case input
|
102
133
|
when Hash
|
103
134
|
node = Node.new(key, "#{path}.#{key}", { type: :object }, self)
|
104
|
-
node.children = { properties: {}, pattern_properties:
|
135
|
+
node.children = { properties: {}, pattern_properties: [] }
|
105
136
|
node
|
106
137
|
when Array
|
107
138
|
node = Node.new(key, :"#{path}.#{key}[]", { type: :array }, self)
|
@@ -111,7 +142,7 @@ module LazyGraph
|
|
111
142
|
when Array then :array
|
112
143
|
end
|
113
144
|
node.children = Node.new(:items, :"#{path}.#{key}[].items", { type: child_type }, node)
|
114
|
-
node.children.children = { properties: {}, pattern_properties:
|
145
|
+
node.children.children = { properties: {}, pattern_properties: [] } if child_type == :object
|
115
146
|
node
|
116
147
|
else
|
117
148
|
Node.new(key, :"#{path}.#{key}", {}, self)
|
@@ -131,7 +162,7 @@ module LazyGraph
|
|
131
162
|
end
|
132
163
|
|
133
164
|
def resolve_input(stack_memory, path, key)
|
134
|
-
input_id = key.object_id ^ stack_memory.shifted_id
|
165
|
+
input_id = key.object_id >> 2 ^ stack_memory.shifted_id
|
135
166
|
if @resolution_stack.include?(input_id)
|
136
167
|
if @debug
|
137
168
|
stack_memory.log_debug(
|
@@ -143,8 +174,9 @@ module LazyGraph
|
|
143
174
|
end
|
144
175
|
|
145
176
|
@resolution_stack << (input_id)
|
146
|
-
first_segment = path.
|
147
|
-
|
177
|
+
first_segment = path.segment.part
|
178
|
+
|
179
|
+
resolver_node = @resolvers[first_segment] ||= (first_segment == key ? parent.parent : @parent).find_resolver_for(first_segment)
|
148
180
|
|
149
181
|
if resolver_node
|
150
182
|
input_frame_pointer = stack_memory.ptr_at(resolver_node.depth)
|
@@ -161,35 +193,13 @@ module LazyGraph
|
|
161
193
|
end
|
162
194
|
|
163
195
|
def ancestors
|
164
|
-
@ancestors ||= [self, *(parent ? parent.ancestors : [])]
|
196
|
+
@ancestors ||= [self, *(@parent ? @parent.ancestors : [])]
|
165
197
|
end
|
166
198
|
|
167
199
|
def find_resolver_for(segment)
|
168
200
|
segment == :'$' ? root : @parent&.find_resolver_for(segment)
|
169
201
|
end
|
170
202
|
|
171
|
-
def cast(value)
|
172
|
-
if @is_decimal
|
173
|
-
value.is_a?(BigDecimal) ? value : value.to_d
|
174
|
-
elsif @is_date
|
175
|
-
value.is_a?(String) ? Date.parse(value) : value
|
176
|
-
elsif @is_boolean
|
177
|
-
if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
178
|
-
value
|
179
|
-
else
|
180
|
-
value.is_a?(MissingValue) ? false : !!value
|
181
|
-
end
|
182
|
-
elsif @is_timestamp
|
183
|
-
case value
|
184
|
-
when String then DateTime.parse(value).to_time
|
185
|
-
when Numeric then Time.at(value)
|
186
|
-
else value
|
187
|
-
end
|
188
|
-
else
|
189
|
-
value
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
203
|
def fetch_item(input, key, stack)
|
194
204
|
return MissingValue { key } unless input
|
195
205
|
|
@@ -214,7 +224,7 @@ module LazyGraph
|
|
214
224
|
end
|
215
225
|
end
|
216
226
|
|
217
|
-
def copy_item!(input, key, stack, (path,
|
227
|
+
def copy_item!(input, key, stack, (path, _i, segment_indexes))
|
218
228
|
if segment_indexes
|
219
229
|
missing_value = nil
|
220
230
|
parts = path.parts.dup
|
@@ -259,7 +269,6 @@ module LazyGraph
|
|
259
269
|
end
|
260
270
|
path = @path_cache[parts_identity] ||= PathParser::Path.new(parts: parts) unless missing_value
|
261
271
|
end
|
262
|
-
|
263
272
|
result = missing_value || resolve_input(stack, path, key)
|
264
273
|
@node_context[i] = result.is_a?(MissingValue) ? nil : result
|
265
274
|
end
|
@@ -267,22 +276,23 @@ module LazyGraph
|
|
267
276
|
@node_context[:itself] = input
|
268
277
|
@node_context[:stack_ptr] = stack
|
269
278
|
|
270
|
-
conditions_passed =
|
271
|
-
allowed_value.is_a?(Array) ? allowed_value.include?(@node_context[field]) : allowed_value
|
272
|
-
end
|
279
|
+
conditions_passed = !(@conditions&.any? do |field, allowed_value|
|
280
|
+
allowed_value.is_a?(Array) ? !allowed_value.include?(@node_context[field]) : allowed_value != @node_context[field]
|
281
|
+
end)
|
273
282
|
|
274
283
|
ex = nil
|
275
284
|
result = \
|
276
285
|
if conditions_passed
|
277
286
|
output = begin
|
278
|
-
cast(@node_context.process!)
|
287
|
+
cast(@fixed_result || @node_context.process!)
|
279
288
|
rescue LazyGraph::AbortError => e
|
280
289
|
raise e
|
281
290
|
rescue StandardError => e
|
282
291
|
ex = e
|
292
|
+
LazyGraph.logger.error(e)
|
293
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
283
294
|
MissingValue { "#{key} raised exception: #{e.message}" }
|
284
295
|
end
|
285
|
-
output = output.dup if @has_properties
|
286
296
|
|
287
297
|
input[key] = output.nil? ? MissingValue { key } : output
|
288
298
|
else
|
@@ -292,22 +302,18 @@ module LazyGraph
|
|
292
302
|
if @debug
|
293
303
|
stack.log_debug(
|
294
304
|
output: :"#{stack}.#{key}",
|
295
|
-
result: result,
|
305
|
+
result: HashUtils.deep_dup(result),
|
296
306
|
inputs: @node_context.to_h.except(:itself, :stack_ptr).transform_keys { |k| @input_mapper&.[](k) || k },
|
297
307
|
calc: @src,
|
298
308
|
**(@conditions ? { conditions: @conditions } : {}),
|
299
|
-
**(
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
else
|
308
|
-
{}
|
309
|
-
end
|
310
|
-
)
|
309
|
+
**(if ex
|
310
|
+
{ exception: ex, backtrace: ex.backtrace.take_while do |line|
|
311
|
+
!line.include?('lazy_graph/node.rb')
|
312
|
+
end }
|
313
|
+
else
|
314
|
+
{}
|
315
|
+
end
|
316
|
+
)
|
311
317
|
)
|
312
318
|
end
|
313
319
|
result
|
@@ -11,10 +11,10 @@ module LazyGraph
|
|
11
11
|
def next = @next ||= parts.length <= 1 ? Path::BLANK : Path.new(parts: parts[1..])
|
12
12
|
def empty? = @empty ||= parts.empty?
|
13
13
|
def segment = @segment ||= parts&.[](0)
|
14
|
-
def index? = @index ||=
|
14
|
+
def index? = @index ||= !empty? && segment&.index?
|
15
15
|
def identity = @identity ||= parts&.each_with_index&.reduce(0) { |acc, (p, i)| acc ^ (p.object_id) << (i * 8) }
|
16
16
|
def map(&block) = empty? ? self : Path.new(parts: parts.map(&block))
|
17
|
-
def shifted_id = @shifted_id ||= object_id <<
|
17
|
+
def shifted_id = @shifted_id ||= object_id << 28
|
18
18
|
|
19
19
|
def merge(other)
|
20
20
|
(@merged ||= {})[other] ||= \
|
data/lib/lazy_graph/server.rb
CHANGED
@@ -11,6 +11,7 @@ module LazyGraph
|
|
11
11
|
|
12
12
|
def call(env)
|
13
13
|
# Rack environment contains request details
|
14
|
+
env[:X_REQUEST_TIME_START] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
14
15
|
request = Rack::Request.new(env)
|
15
16
|
|
16
17
|
unless (graph_module = @routes[request.path.to_sym])
|
@@ -73,13 +74,17 @@ module LazyGraph
|
|
73
74
|
error!(request, 404, 'Not Found', details)
|
74
75
|
end
|
75
76
|
|
77
|
+
def request_ms(request)
|
78
|
+
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - request.env[:X_REQUEST_TIME_START]) * 1000.0).round(3)
|
79
|
+
end
|
80
|
+
|
76
81
|
def success!(request, result, status: 200)
|
77
|
-
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status}")
|
82
|
+
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status} #{request_ms(request)}ms")
|
78
83
|
[status, { 'Content-Type' => 'text/json' }, [result.to_json]]
|
79
84
|
end
|
80
85
|
|
81
86
|
def error!(request, status, message, details = '')
|
82
|
-
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status}")
|
87
|
+
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status} #{request_ms(request)}ms")
|
83
88
|
[status, { 'Content-Type' => 'text/json' }, [{ 'error': message, 'details': details }.to_json]]
|
84
89
|
end
|
85
90
|
end
|
@@ -5,15 +5,21 @@ module LazyGraph
|
|
5
5
|
POINTER_POOL = []
|
6
6
|
|
7
7
|
StackPointer = Struct.new(:parent, :frame, :depth, :key, :root) do
|
8
|
-
|
8
|
+
attr_accessor :pointer_cache
|
9
|
+
|
10
|
+
def shifted_id = @shifted_id ||= object_id << 28
|
9
11
|
|
10
12
|
def push(frame, key)
|
11
|
-
(POINTER_POOL.pop
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
if (ptr = POINTER_POOL.pop)
|
14
|
+
ptr.parent = self
|
15
|
+
ptr.parent = self
|
16
|
+
ptr.frame = frame
|
17
|
+
ptr.key = key
|
18
|
+
ptr.depth = depth + 1
|
19
|
+
ptr.pointer_cache&.clear
|
20
|
+
ptr
|
21
|
+
else
|
22
|
+
StackPointer.new(parent: self, frame: frame, key: key, depth: depth + 1, root: root || self)
|
17
23
|
end
|
18
24
|
end
|
19
25
|
|
@@ -23,9 +29,8 @@ module LazyGraph
|
|
23
29
|
end
|
24
30
|
|
25
31
|
def ptr_at(index)
|
26
|
-
|
27
|
-
|
28
|
-
parent&.ptr_at(index)
|
32
|
+
@pointer_cache ||= {}.compare_by_identity
|
33
|
+
@pointer_cache[index] ||= depth == index ? self : parent&.ptr_at(index)
|
29
34
|
end
|
30
35
|
|
31
36
|
def method_missing(name, *args, &block)
|
@@ -38,9 +43,14 @@ module LazyGraph
|
|
38
43
|
end
|
39
44
|
end
|
40
45
|
|
46
|
+
def index
|
47
|
+
key
|
48
|
+
end
|
49
|
+
|
41
50
|
def log_debug(**log_item)
|
42
51
|
root.frame[:DEBUG] = [] if !root.frame[:DEBUG] || root.frame[:DEBUG].is_a?(MissingValue)
|
43
52
|
root.frame[:DEBUG] << { **log_item, location: to_s }
|
53
|
+
nil
|
44
54
|
end
|
45
55
|
|
46
56
|
def respond_to_missing?(name, include_private = false)
|
data/lib/lazy_graph/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lazy_graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date: 2024-12-
|
10
|
+
date: 2024-12-25 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: json-schema
|
@@ -164,6 +163,7 @@ files:
|
|
164
163
|
- lib/lazy_graph/node/derived_rules.rb
|
165
164
|
- lib/lazy_graph/node/node_properties.rb
|
166
165
|
- lib/lazy_graph/node/object_node.rb
|
166
|
+
- lib/lazy_graph/node/symbol_hash.rb
|
167
167
|
- lib/lazy_graph/path_parser.rb
|
168
168
|
- lib/lazy_graph/path_parser/path.rb
|
169
169
|
- lib/lazy_graph/path_parser/path_group.rb
|
@@ -178,7 +178,6 @@ licenses:
|
|
178
178
|
metadata:
|
179
179
|
homepage_uri: https://github.com/wouterken/lazy_graph
|
180
180
|
source_code_uri: https://github.com/wouterken/lazy_graph
|
181
|
-
post_install_message:
|
182
181
|
rdoc_options: []
|
183
182
|
require_paths:
|
184
183
|
- lib
|
@@ -193,8 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
192
|
- !ruby/object:Gem::Version
|
194
193
|
version: '0'
|
195
194
|
requirements: []
|
196
|
-
rubygems_version: 3.
|
197
|
-
signing_key:
|
195
|
+
rubygems_version: 3.6.2
|
198
196
|
specification_version: 4
|
199
197
|
summary: JSON Driven, Stateless Rules Engine
|
200
198
|
test_files: []
|