lazy_graph 0.1.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 +7 -0
- data/.rubocop.yml +81 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +562 -0
- data/Rakefile +4 -0
- data/examples/performance_tests.rb +117 -0
- data/lib/lazy_graph/builder/dsl.rb +315 -0
- data/lib/lazy_graph/builder.rb +138 -0
- data/lib/lazy_graph/builder_group.rb +57 -0
- data/lib/lazy_graph/context.rb +60 -0
- data/lib/lazy_graph/graph.rb +73 -0
- data/lib/lazy_graph/hash_utils.rb +87 -0
- data/lib/lazy_graph/lazy-graph.json +148 -0
- data/lib/lazy_graph/missing_value.rb +26 -0
- data/lib/lazy_graph/node/array_node.rb +67 -0
- data/lib/lazy_graph/node/derived_rules.rb +196 -0
- data/lib/lazy_graph/node/node_properties.rb +64 -0
- data/lib/lazy_graph/node/object_node.rb +113 -0
- data/lib/lazy_graph/node.rb +316 -0
- data/lib/lazy_graph/path_parser/path.rb +46 -0
- data/lib/lazy_graph/path_parser/path_group.rb +12 -0
- data/lib/lazy_graph/path_parser/path_part.rb +13 -0
- data/lib/lazy_graph/path_parser.rb +211 -0
- data/lib/lazy_graph/server.rb +86 -0
- data/lib/lazy_graph/stack_pointer.rb +56 -0
- data/lib/lazy_graph/version.rb +5 -0
- data/lib/lazy_graph.rb +32 -0
- data/logo.png +0 -0
- metadata +200 -0
@@ -0,0 +1,211 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# LazyGraph supports a bespoke path structure for querying a LazyGraph.
|
4
|
+
# It has some overlap with
|
5
|
+
# * JSON path (but supports querying a subset of object properties), and
|
6
|
+
# * GraphQL (but it supports direct references to deeply nested properties without preserving structure)/
|
7
|
+
#
|
8
|
+
# Example query paths and outputs
|
9
|
+
#
|
10
|
+
# "employees" => {"employees": [{...whole object...}, {...whole object...}]}
|
11
|
+
# "employees[id,name]" => {"employees": [{ "id": "3", "name": "John Smith}, { "id": "4", "name": "Jane Smith}]}
|
12
|
+
# "employees[0..1] => {"employees": [{...whole object...}, {...whole object...}]}"
|
13
|
+
# "employees[0..1] => {"employees": [{...whole object...}, {...whole object...}]}"
|
14
|
+
#
|
15
|
+
module LazyGraph
|
16
|
+
module PathParser
|
17
|
+
require_relative 'path_parser/path'
|
18
|
+
require_relative 'path_parser/path_group'
|
19
|
+
require_relative 'path_parser/path_part'
|
20
|
+
# This module is responsible for parsing complex path strings into structured components.
|
21
|
+
# Public class method to parse the path string
|
22
|
+
def self.parse(path, strip_root = false)
|
23
|
+
return Path::BLANK if path.nil? || path.empty?
|
24
|
+
|
25
|
+
start = strip_root && path.to_s.start_with?('$.') ? 2 : 0
|
26
|
+
parse_recursive(path, start)
|
27
|
+
end
|
28
|
+
|
29
|
+
class << self
|
30
|
+
# Recursively parses the path starting from index 'start'
|
31
|
+
# Returns [Path object, new_index]
|
32
|
+
def parse_recursive(path, start)
|
33
|
+
parse_structure = {
|
34
|
+
parts: [],
|
35
|
+
buffer: ''.dup,
|
36
|
+
i: start
|
37
|
+
}
|
38
|
+
|
39
|
+
parse_main_loop(path, parse_structure)
|
40
|
+
parse_finalize(parse_structure)
|
41
|
+
|
42
|
+
Path.new(parts: parse_structure[:parts])
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_main_loop(path, parse_structure)
|
46
|
+
while parse_structure[:i] < path.length
|
47
|
+
char = path[parse_structure[:i]]
|
48
|
+
handle_char(char, path, parse_structure)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle_char(char, path, structure)
|
53
|
+
case char
|
54
|
+
when '.'
|
55
|
+
handle_dot(path, structure)
|
56
|
+
when '['
|
57
|
+
handle_open_bracket(path, structure)
|
58
|
+
when ','
|
59
|
+
handle_comma(structure)
|
60
|
+
when ']'
|
61
|
+
handle_close_bracket(structure)
|
62
|
+
else
|
63
|
+
structure[:buffer] += char
|
64
|
+
structure[:i] += 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def handle_dot(path, structure)
|
69
|
+
# Check if it's part of a range ('..' or '...')
|
70
|
+
if path[structure[:i] + 1] == '.'
|
71
|
+
handle_range_dot(path, structure)
|
72
|
+
else
|
73
|
+
handle_single_dot(structure)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_range_dot(path, structure)
|
78
|
+
if path[structure[:i] + 2] == '.'
|
79
|
+
structure[:buffer] += '...'
|
80
|
+
structure[:i] += 3
|
81
|
+
else
|
82
|
+
structure[:buffer] += '..'
|
83
|
+
structure[:i] += 2
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_single_dot(structure)
|
88
|
+
unless structure[:buffer].strip.empty?
|
89
|
+
parsed = parse_buffer(structure[:buffer].strip)
|
90
|
+
append_parsed(parsed, structure[:parts])
|
91
|
+
structure[:buffer] = ''.dup
|
92
|
+
end
|
93
|
+
structure[:i] += 1
|
94
|
+
end
|
95
|
+
|
96
|
+
def append_parsed(parsed, parts)
|
97
|
+
if parsed.is_a?(Array)
|
98
|
+
parsed.each { |p| parts << p }
|
99
|
+
elsif parsed.is_a?(Range)
|
100
|
+
parts << PathGroup.new(options: parsed.map { |p| Path.new(parts: [PathPart.new(part: p.to_sym)]) })
|
101
|
+
else
|
102
|
+
parts << parsed
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_open_bracket(path, structure)
|
107
|
+
unless structure[:buffer].strip.empty?
|
108
|
+
paths = structure[:buffer].strip.split('.').map(&:strip)
|
109
|
+
paths.each { |p| structure[:parts] << PathPart.new(part: p.to_sym) }
|
110
|
+
structure[:buffer] = ''.dup
|
111
|
+
end
|
112
|
+
|
113
|
+
closing_bracket = find_matching_bracket(path, structure[:i])
|
114
|
+
raise 'Unbalanced brackets in path.' if closing_bracket == -1
|
115
|
+
|
116
|
+
inside = path[(structure[:i] + 1)...closing_bracket]
|
117
|
+
elements = split_by_comma(inside)
|
118
|
+
parsed_elements = elements.map { |el| parse_recursive(el, 0) }
|
119
|
+
path_group = PathGroup.new(options: parsed_elements)
|
120
|
+
structure[:parts] << path_group
|
121
|
+
structure[:i] = closing_bracket + 1
|
122
|
+
end
|
123
|
+
|
124
|
+
def handle_comma(structure)
|
125
|
+
unless structure[:buffer].strip.empty?
|
126
|
+
parsed = parse_buffer(structure[:buffer].strip)
|
127
|
+
append_parsed(parsed, structure[:parts])
|
128
|
+
structure[:buffer] = ''.dup
|
129
|
+
end
|
130
|
+
structure[:i] += 1
|
131
|
+
end
|
132
|
+
|
133
|
+
def handle_close_bracket(structure)
|
134
|
+
raise 'Unbalanced closing bracket in path.' if structure[:buffer].strip.empty?
|
135
|
+
|
136
|
+
parsed = parse_buffer(structure[:buffer].strip)
|
137
|
+
append_parsed(parsed, structure[:parts])
|
138
|
+
structure[:buffer] = ''.dup
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse_finalize(structure)
|
142
|
+
return if structure[:buffer].strip.empty?
|
143
|
+
|
144
|
+
parsed = parse_buffer(structure[:buffer].strip)
|
145
|
+
append_parsed(parsed, structure[:parts])
|
146
|
+
end
|
147
|
+
|
148
|
+
def find_matching_bracket(path, start)
|
149
|
+
depth = 1
|
150
|
+
i = start + 1
|
151
|
+
while i < path.length
|
152
|
+
if path[i] == '['
|
153
|
+
depth += 1
|
154
|
+
elsif path[i] == ']'
|
155
|
+
depth -= 1
|
156
|
+
return i if depth.zero?
|
157
|
+
end
|
158
|
+
i += 1
|
159
|
+
end
|
160
|
+
-1 # No matching closing bracket found
|
161
|
+
end
|
162
|
+
|
163
|
+
def split_by_comma(str)
|
164
|
+
elements = []
|
165
|
+
buffer = ''.dup
|
166
|
+
depth = 0
|
167
|
+
str.each_char do |c|
|
168
|
+
case c
|
169
|
+
when '['
|
170
|
+
depth += 1
|
171
|
+
buffer << c
|
172
|
+
when ']'
|
173
|
+
depth -= 1
|
174
|
+
buffer << c
|
175
|
+
when ','
|
176
|
+
if depth.zero?
|
177
|
+
elements << buffer.strip
|
178
|
+
buffer = ''.dup
|
179
|
+
else
|
180
|
+
buffer << c
|
181
|
+
end
|
182
|
+
else
|
183
|
+
buffer << c
|
184
|
+
end
|
185
|
+
end
|
186
|
+
elements << buffer.strip unless buffer.strip.empty?
|
187
|
+
elements
|
188
|
+
end
|
189
|
+
|
190
|
+
def parse_buffer(buffer)
|
191
|
+
if buffer.include?('...')
|
192
|
+
parse_range(buffer, '...', true)
|
193
|
+
elsif buffer.include?('..')
|
194
|
+
parse_range(buffer, '..', false)
|
195
|
+
elsif buffer.include?('.')
|
196
|
+
paths = buffer.split('.').map(&:strip)
|
197
|
+
paths.map { |p| PathPart.new(part: p.to_sym) }
|
198
|
+
else
|
199
|
+
PathPart.new(part: buffer.to_sym)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def parse_range(buffer, delimiter, exclude_end)
|
204
|
+
parts = buffer.split(delimiter)
|
205
|
+
return buffer unless parts.size == 2
|
206
|
+
|
207
|
+
Range.new(parts[0].strip, parts[1].strip, exclude_end)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LazyGraph
|
4
|
+
class Server
|
5
|
+
ALLOWED_VALUES_VALIDATE = [true, false, nil, 'input', 'context'].to_set.freeze
|
6
|
+
ALLOWED_VALUES_DEBUG = [true, false, nil].to_set.freeze
|
7
|
+
|
8
|
+
def initialize(routes: {})
|
9
|
+
@routes = routes.transform_keys(&:to_sym).compare_by_identity
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
# Rack environment contains request details
|
14
|
+
request = Rack::Request.new(env)
|
15
|
+
|
16
|
+
unless (graph_module = @routes[request.path.to_sym])
|
17
|
+
return not_found!(request.path)
|
18
|
+
end
|
19
|
+
|
20
|
+
return success!(request, graph_module.usage) if request.get?
|
21
|
+
|
22
|
+
if request.post?
|
23
|
+
body = JSON.parse(request.body.read)
|
24
|
+
context, modules, validate, debug, query = body.values_at('context', 'modules', 'validate', 'debug', 'query')
|
25
|
+
unless context.is_a?(Hash) && !context.empty?
|
26
|
+
return not_acceptable!(request, "Invalid 'context' Parameter", 'Should be a non-empty object.')
|
27
|
+
end
|
28
|
+
|
29
|
+
unless modules.is_a?(Hash) && !modules.empty?
|
30
|
+
return not_acceptable!(request, "Invalid 'modules' Parameter", 'Should be a non-empty object.')
|
31
|
+
end
|
32
|
+
|
33
|
+
unless ALLOWED_VALUES_VALIDATE.include?(validate)
|
34
|
+
return not_acceptable!(
|
35
|
+
request, "Invalid 'validate' Parameter", "Should be nil, bool, or one of 'input', 'context'"
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
unless ALLOWED_VALUES_DEBUG.include?(debug)
|
40
|
+
return not_acceptable!(request, "Invalid 'debug' Parameter", 'Should be nil or bool')
|
41
|
+
end
|
42
|
+
|
43
|
+
unless query.nil? || query.is_a?(String) || (query.is_a?(Array) && query.all? do |q|
|
44
|
+
q.is_a?(String)
|
45
|
+
end)
|
46
|
+
return not_acceptable!(request, "Invalid 'query' Parameter", 'Should be nil, array or string array')
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
result = graph_module.eval!(
|
51
|
+
modules: modules, context: context,
|
52
|
+
validate: validate, debug: debug,
|
53
|
+
query: query
|
54
|
+
)
|
55
|
+
return not_acceptable!(request, result[:message], result[:detail]) if result[:type] == :error
|
56
|
+
|
57
|
+
return success!(request, result)
|
58
|
+
rescue StandardError => e
|
59
|
+
LazyGraph.logger.error(e.message)
|
60
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
61
|
+
return error!(request, 500, 'Internal Server Error', e.message)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
not_found!("#{request.request_method} #{request.path}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def not_acceptable!(request, message, details = '')
|
69
|
+
error!(request, 406, message, details)
|
70
|
+
end
|
71
|
+
|
72
|
+
def not_found!(request, details = '')
|
73
|
+
error!(request, 404, 'Not Found', details)
|
74
|
+
end
|
75
|
+
|
76
|
+
def success!(request, result, status: 200)
|
77
|
+
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status}")
|
78
|
+
[status, { 'Content-Type' => 'text/json' }, [result.to_json]]
|
79
|
+
end
|
80
|
+
|
81
|
+
def error!(request, status, message, details = '')
|
82
|
+
LazyGraph.logger.info("#{request.request_method}: #{request.path} => #{status}")
|
83
|
+
[status, { 'Content-Type' => 'text/json' }, [{ 'error': message, 'details': details }.to_json]]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LazyGraph
|
4
|
+
# Module to provide lazy graph functionalities using stack pointers.
|
5
|
+
POINTER_POOL = []
|
6
|
+
|
7
|
+
StackPointer = Struct.new(:parent, :frame, :depth, :key, :root) do
|
8
|
+
def push(frame, key)
|
9
|
+
(POINTER_POOL.pop || StackPointer.new).tap do |pointer|
|
10
|
+
pointer.parent = self
|
11
|
+
pointer.frame = frame
|
12
|
+
pointer.key = key
|
13
|
+
pointer.depth = depth + 1
|
14
|
+
pointer.root = root || self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def recycle!
|
19
|
+
POINTER_POOL.push(self)
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def ptr_at(index)
|
24
|
+
return self if depth == index
|
25
|
+
|
26
|
+
parent&.ptr_at(index)
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(name, *args, &block)
|
30
|
+
if frame.respond_to?(name)
|
31
|
+
frame.send(name, *args, &block)
|
32
|
+
elsif parent
|
33
|
+
parent.send(name, *args, &block)
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_debug(**log_item)
|
40
|
+
root.frame[:DEBUG] = [] if !root.frame[:DEBUG] || root.frame[:DEBUG].is_a?(MissingValue)
|
41
|
+
root.frame[:DEBUG] << { **log_item, location: to_s }
|
42
|
+
end
|
43
|
+
|
44
|
+
def respond_to_missing?(name, include_private = false)
|
45
|
+
frame.respond_to?(name, include_private) || parent.respond_to?(name, include_private)
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
if parent
|
50
|
+
"#{parent}#{key.to_s =~ /\d+/ ? "[#{key}]" : ".#{key}"}"
|
51
|
+
else
|
52
|
+
key.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/lazy_graph.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
# LazyGraph is a library which allows you to define a strictly typed hierarchical Graph structure.
|
6
|
+
# Within this graph you can annotate certain nodes as derived nodes, which compute their outputs based
|
7
|
+
# on input dependencies on other nodes in the graph structure (can be relative and absolute references).
|
8
|
+
# Dependencies can be nested any level deep (but cannot be circular).
|
9
|
+
#
|
10
|
+
# You can then provide a subset of the graph as input context, and extract calculated outputs from the same graph
|
11
|
+
# which will be lazily computed (only derived rules actually needed for the queried output is computed).
|
12
|
+
#
|
13
|
+
module LazyGraph
|
14
|
+
class AbortError < StandardError; end
|
15
|
+
class << self
|
16
|
+
attr_accessor :logger
|
17
|
+
end
|
18
|
+
|
19
|
+
self.logger = Logger.new($stdout)
|
20
|
+
end
|
21
|
+
|
22
|
+
require_relative 'lazy_graph/context'
|
23
|
+
require_relative 'lazy_graph/missing_value'
|
24
|
+
require_relative 'lazy_graph/node'
|
25
|
+
require_relative 'lazy_graph/graph'
|
26
|
+
require_relative 'lazy_graph/version'
|
27
|
+
require_relative 'lazy_graph/path_parser'
|
28
|
+
require_relative 'lazy_graph/hash_utils'
|
29
|
+
require_relative 'lazy_graph/builder'
|
30
|
+
require_relative 'lazy_graph/builder_group'
|
31
|
+
require_relative 'lazy_graph/stack_pointer'
|
32
|
+
require_relative 'lazy_graph/server'
|
data/logo.png
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lazy_graph
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Wouter Coppieters
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json-schema
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: logger
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: prism
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rack
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: ostruct
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fiddle
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: debug
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rdoc
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: JSON Driven, Stateless Rules Engine for JIT and efficient evaluation
|
140
|
+
of complex rules and computation graphs.
|
141
|
+
email:
|
142
|
+
- wc@pico.net.nz
|
143
|
+
executables: []
|
144
|
+
extensions: []
|
145
|
+
extra_rdoc_files: []
|
146
|
+
files:
|
147
|
+
- ".rubocop.yml"
|
148
|
+
- CODE_OF_CONDUCT.md
|
149
|
+
- LICENSE.txt
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- examples/performance_tests.rb
|
153
|
+
- lib/lazy_graph.rb
|
154
|
+
- lib/lazy_graph/builder.rb
|
155
|
+
- lib/lazy_graph/builder/dsl.rb
|
156
|
+
- lib/lazy_graph/builder_group.rb
|
157
|
+
- lib/lazy_graph/context.rb
|
158
|
+
- lib/lazy_graph/graph.rb
|
159
|
+
- lib/lazy_graph/hash_utils.rb
|
160
|
+
- lib/lazy_graph/lazy-graph.json
|
161
|
+
- lib/lazy_graph/missing_value.rb
|
162
|
+
- lib/lazy_graph/node.rb
|
163
|
+
- lib/lazy_graph/node/array_node.rb
|
164
|
+
- lib/lazy_graph/node/derived_rules.rb
|
165
|
+
- lib/lazy_graph/node/node_properties.rb
|
166
|
+
- lib/lazy_graph/node/object_node.rb
|
167
|
+
- lib/lazy_graph/path_parser.rb
|
168
|
+
- lib/lazy_graph/path_parser/path.rb
|
169
|
+
- lib/lazy_graph/path_parser/path_group.rb
|
170
|
+
- lib/lazy_graph/path_parser/path_part.rb
|
171
|
+
- lib/lazy_graph/server.rb
|
172
|
+
- lib/lazy_graph/stack_pointer.rb
|
173
|
+
- lib/lazy_graph/version.rb
|
174
|
+
- logo.png
|
175
|
+
homepage: https://github.com/wouterken/lazy_graph
|
176
|
+
licenses:
|
177
|
+
- MIT
|
178
|
+
metadata:
|
179
|
+
homepage_uri: https://github.com/wouterken/lazy_graph
|
180
|
+
source_code_uri: https://github.com/wouterken/lazy_graph
|
181
|
+
post_install_message:
|
182
|
+
rdoc_options: []
|
183
|
+
require_paths:
|
184
|
+
- lib
|
185
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
186
|
+
requirements:
|
187
|
+
- - ">="
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 3.0.0
|
190
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
requirements: []
|
196
|
+
rubygems_version: 3.5.22
|
197
|
+
signing_key:
|
198
|
+
specification_version: 4
|
199
|
+
summary: JSON Driven, Stateless Rules Engine
|
200
|
+
test_files: []
|