lazy_graph 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|