lazy_graph 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
@@ -0,0 +1,138 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
module LazyGraph
|
6
|
+
class RackApp
|
7
|
+
ALLOWED_VALUES_VALIDATE = [true, false, nil, 'input', 'context'].to_set.freeze
|
8
|
+
ALLOWED_VALUES_DEBUG = [true, false, nil].to_set.freeze
|
9
|
+
|
10
|
+
attr_reader :routes
|
11
|
+
|
12
|
+
def initialize(routes: {})
|
13
|
+
@routes = routes.transform_keys(&:to_sym).compare_by_identity
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
env[:X_REQUEST_TIME_START] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
18
|
+
request = Rack::Request.new(env)
|
19
|
+
|
20
|
+
return routes!(request) if (request.path == '/routes' || request.path == '/') && request.get?
|
21
|
+
return health!(request) if request.path == '/health' && request.get?
|
22
|
+
return not_found!(request) unless (graph_module = @routes[request.path.to_sym])
|
23
|
+
return success!(request, graph_module.usage) if request.get?
|
24
|
+
return not_found!("#{request.request_method} #{request.path}") unless request.post?
|
25
|
+
|
26
|
+
query_lazy_graph!(request, graph_module)
|
27
|
+
end
|
28
|
+
|
29
|
+
def query_lazy_graph!(request, graph_module)
|
30
|
+
body = begin
|
31
|
+
JSON.parse(request.body.read, symbolize_names: true)
|
32
|
+
rescue JSON::ParserError => e
|
33
|
+
return not_acceptable!(request, 'Invalid JSON', e.message)
|
34
|
+
end
|
35
|
+
context, modules, validate, debug, query = body.values_at(:context, :modules, :validate, :debug, :query)
|
36
|
+
unless context.is_a?(Hash) && !context.empty?
|
37
|
+
return not_acceptable!(request, "Invalid 'context' Parameter", 'Should be a non-empty object.')
|
38
|
+
end
|
39
|
+
|
40
|
+
unless (modules.is_a?(Hash) && !modules.empty?) || modules.is_a?(String) || (modules.is_a?(Array) && modules.all? do |m|
|
41
|
+
m.is_a?(String)
|
42
|
+
end)
|
43
|
+
return not_acceptable!(request, "Invalid 'modules' Parameter",
|
44
|
+
'Should be a string, string-array or non-empty object.')
|
45
|
+
end
|
46
|
+
|
47
|
+
modules = Array(modules).map { |m| [m, {}] }.to_h unless modules.is_a?(Hash)
|
48
|
+
|
49
|
+
unless ALLOWED_VALUES_VALIDATE.include?(validate)
|
50
|
+
return not_acceptable!(
|
51
|
+
request, "Invalid 'validate' Parameter", "Should be nil, bool, or one of 'input', 'context'"
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
unless ALLOWED_VALUES_DEBUG.include?(debug) || debug.is_a?(String)
|
56
|
+
return not_acceptable!(request, "Invalid 'debug' Parameter", 'Should be nil or bool')
|
57
|
+
end
|
58
|
+
|
59
|
+
debug = Regexp.new(Regexp.escape(debug)) if debug.is_a?(String) && debug != 'exceptions'
|
60
|
+
|
61
|
+
unless query.nil? || query.is_a?(String) || (query.is_a?(Array) && query.all? do |q|
|
62
|
+
q.is_a?(String)
|
63
|
+
end)
|
64
|
+
return not_acceptable!(request, "Invalid 'query' Parameter", 'Should be nil, array or string array')
|
65
|
+
end
|
66
|
+
|
67
|
+
begin
|
68
|
+
result = graph_module.eval!(
|
69
|
+
modules: modules,
|
70
|
+
context: context,
|
71
|
+
validate: validate.nil? ? true : validate,
|
72
|
+
debug: if !debug.nil?
|
73
|
+
debug
|
74
|
+
else
|
75
|
+
LazyGraph::Environment.development? ? 'exceptions' : debug
|
76
|
+
end,
|
77
|
+
query: query
|
78
|
+
)
|
79
|
+
return not_acceptable!(request, result[:message], result[:detail]) if result[:type] == :error
|
80
|
+
|
81
|
+
success!(request, result)
|
82
|
+
rescue AbortError, ValidationError => e
|
83
|
+
LazyGraph.logger.error(e.message)
|
84
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
85
|
+
error!(request, 400, 'Bad Request', e.message)
|
86
|
+
rescue StandardError => e
|
87
|
+
LazyGraph.logger.error(e.message)
|
88
|
+
LazyGraph.logger.error(e.backtrace.join("\n"))
|
89
|
+
|
90
|
+
error!(request, 500, 'Internal Server Error', e.message)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def routes!(request)
|
95
|
+
success!(request, @routes.keys.map { |k, _v| { route: k.to_s, methods: %i[GET POST] } })
|
96
|
+
end
|
97
|
+
|
98
|
+
def health!(request)
|
99
|
+
success!(request, { status: 'ok' })
|
100
|
+
end
|
101
|
+
|
102
|
+
def not_acceptable!(request, message, details = '')
|
103
|
+
error!(request, 406, message, details)
|
104
|
+
end
|
105
|
+
|
106
|
+
def not_found!(request, details = '')
|
107
|
+
error!(request, 404, 'Not Found', details)
|
108
|
+
end
|
109
|
+
|
110
|
+
def request_ms(request)
|
111
|
+
((Process.clock_gettime(Process::CLOCK_MONOTONIC) - request.env[:X_REQUEST_TIME_START]) * 1000.0).round(3)
|
112
|
+
end
|
113
|
+
|
114
|
+
def success!(request, result, status: 200)
|
115
|
+
req_ms = request_ms(request)
|
116
|
+
|
117
|
+
LazyGraph.logger.info(
|
118
|
+
Logger.build_color_string do
|
119
|
+
"#{bold(request.request_method)}: #{dim(request.path)} => #{green(status)} #{light_gray("#{req_ms}ms")}"
|
120
|
+
end
|
121
|
+
)
|
122
|
+
|
123
|
+
[status, { 'content-type' => 'application/json' }, [JSON.fast_generate(result)]]
|
124
|
+
end
|
125
|
+
|
126
|
+
def error!(request, status, message, details = '')
|
127
|
+
req_ms = request_ms(request)
|
128
|
+
|
129
|
+
LazyGraph.logger.error(
|
130
|
+
Logger.build_color_string do
|
131
|
+
"#{bold(request.request_method)}: #{dim(request.path)} => #{status.to_i >= 500 ? red(status) : orange(status)} #{light_gray("#{req_ms}ms")}"
|
132
|
+
end
|
133
|
+
)
|
134
|
+
|
135
|
+
[status, { 'content-type' => 'application/json' }, [JSON.fast_generate({ 'error': message, 'details': details })]]
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'socket'
|
4
|
+
|
5
|
+
module LazyGraph
|
6
|
+
class RackServer
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
@running = false
|
10
|
+
@threads = [] #
|
11
|
+
@threads_m = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def start(port: 9292, workers: 4)
|
15
|
+
trap_signals
|
16
|
+
|
17
|
+
@port = port
|
18
|
+
@workers = workers
|
19
|
+
|
20
|
+
if workers > 1
|
21
|
+
puts "Starting Raxx server with #{workers} processes on port #{port}..."
|
22
|
+
@server = TCPServer.new(@port)
|
23
|
+
@server.listen(1024)
|
24
|
+
|
25
|
+
workers.times do
|
26
|
+
fork(&method(:run_accept_loop))
|
27
|
+
end
|
28
|
+
Process.waitall
|
29
|
+
else
|
30
|
+
puts "Starting single-process server on port #{port}..."
|
31
|
+
@server = TCPServer.new(@port)
|
32
|
+
@server.listen(1024)
|
33
|
+
run_accept_loop
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
#
|
40
|
+
# Main accept loop
|
41
|
+
#
|
42
|
+
def run_accept_loop
|
43
|
+
enable_reuse(@server)
|
44
|
+
@running = true
|
45
|
+
puts "[PID #{Process.pid}] Listening on port #{@port}..."
|
46
|
+
|
47
|
+
while @running
|
48
|
+
begin
|
49
|
+
client = @server.accept_nonblock if @running
|
50
|
+
rescue IO::WaitReadable, Errno::EINTR
|
51
|
+
IO.select([@server], nil, nil, 0.1) if @running
|
52
|
+
retry
|
53
|
+
end
|
54
|
+
|
55
|
+
# Handle connection in a new thread
|
56
|
+
next unless client
|
57
|
+
|
58
|
+
thr = Thread.start(client) do |socket|
|
59
|
+
socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
60
|
+
handle_request(socket)
|
61
|
+
socket.close
|
62
|
+
rescue Errno::ECONNRESET, Errno::EPIPE, IOError, Errno::EINVAL
|
63
|
+
# Connection reset by peer - ignore
|
64
|
+
ensure
|
65
|
+
remove_thread(Thread.current)
|
66
|
+
end
|
67
|
+
|
68
|
+
add_thread(thr)
|
69
|
+
end
|
70
|
+
ensure
|
71
|
+
if @server
|
72
|
+
puts "[PID #{Process.pid}] Shutting down server socket..."
|
73
|
+
@server.close
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Actually handle requests in a keep-alive loop
|
79
|
+
#
|
80
|
+
def handle_request(socket)
|
81
|
+
while @running
|
82
|
+
|
83
|
+
begin
|
84
|
+
request_line = socket.gets if @running
|
85
|
+
rescue IO::WaitReadable, Errno::EINTR, Errno::EPIPE
|
86
|
+
IO.select([socket], nil, nil, 0.5) if @running
|
87
|
+
retry
|
88
|
+
end
|
89
|
+
|
90
|
+
break if request_line.nil? || request_line.strip.empty?
|
91
|
+
|
92
|
+
method, path, http_version = request_line.split
|
93
|
+
|
94
|
+
# Parse headers
|
95
|
+
headers = {}
|
96
|
+
content_length = 0
|
97
|
+
while (line = socket.gets) && line != "\r\n"
|
98
|
+
key, value = line.split(': ', 2)
|
99
|
+
headers[key.downcase] = value.strip
|
100
|
+
content_length = value.to_i if key == 'Content-Length'
|
101
|
+
end
|
102
|
+
|
103
|
+
body = content_length.positive? ? socket.read(content_length) : (+'').force_encoding('ASCII-8BIT')
|
104
|
+
|
105
|
+
# Build Rack environment
|
106
|
+
env = {
|
107
|
+
'REQUEST_METHOD' => method,
|
108
|
+
'PATH_INFO' => path,
|
109
|
+
'HTTP_VERSION' => http_version,
|
110
|
+
'QUERY_STRING' => path[/\?.*/].to_s,
|
111
|
+
'SERVER_PROTOCOL' => 'HTTP/1.1',
|
112
|
+
'SERVER_NAME' => 'localhost',
|
113
|
+
'rack.url_scheme' => 'http',
|
114
|
+
'rack.request.headers' => headers,
|
115
|
+
'rack.input' => StringIO.new(body),
|
116
|
+
'rack.errors' => StringIO.new('')
|
117
|
+
}
|
118
|
+
|
119
|
+
# Call the Rack app
|
120
|
+
status, response_headers, body_enum = @app.call(env)
|
121
|
+
|
122
|
+
content = body_enum.to_enum(:each).map(&:to_s).join
|
123
|
+
|
124
|
+
response_headers['Connection'] = 'keep-alive'
|
125
|
+
response_headers['Content-Length'] = content.bytesize.to_s
|
126
|
+
|
127
|
+
# Write the response
|
128
|
+
socket.print "HTTP/1.1 #{status} #{http_status_message(status)}\r\n"
|
129
|
+
response_headers.each { |k, v| socket.print "#{k}: #{v}\r\n" }
|
130
|
+
socket.print "\r\n"
|
131
|
+
socket.print content
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Thread management helpers
|
137
|
+
#
|
138
|
+
def add_thread(thr)
|
139
|
+
@threads_m.synchronize { @threads << thr }
|
140
|
+
end
|
141
|
+
|
142
|
+
def remove_thread(thr)
|
143
|
+
@threads_m.synchronize { @threads.delete(thr) }
|
144
|
+
end
|
145
|
+
|
146
|
+
#
|
147
|
+
# Trap signals, set @running = false, then force-kill leftover threads
|
148
|
+
#
|
149
|
+
def trap_signals
|
150
|
+
%w[INT TERM].each do |signal|
|
151
|
+
trap(signal) do
|
152
|
+
@running = false
|
153
|
+
# Give threads a grace period to finish
|
154
|
+
graceful_shutdown_threads(timeout: 5)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Gracefully wait for each thread to finish; then force-kill if still alive
|
161
|
+
#
|
162
|
+
def graceful_shutdown_threads(timeout:)
|
163
|
+
@threads.each do |thr|
|
164
|
+
next unless thr.alive?
|
165
|
+
|
166
|
+
# Try a graceful join
|
167
|
+
thr.join(timeout)
|
168
|
+
# If still running after timeout, forcibly kill
|
169
|
+
if thr.alive?
|
170
|
+
puts "[PID #{Process.pid}] Force-killing thread #{thr.object_id}"
|
171
|
+
thr.kill
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
#
|
177
|
+
# Reuse port if supported
|
178
|
+
#
|
179
|
+
def enable_reuse(server)
|
180
|
+
server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true)
|
181
|
+
begin
|
182
|
+
server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEPORT, true)
|
183
|
+
rescue StandardError
|
184
|
+
# Not supported on all systems
|
185
|
+
nil
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Utility: Convert a status code to reason phrase
|
191
|
+
#
|
192
|
+
def http_status_message(status)
|
193
|
+
{
|
194
|
+
200 => 'OK',
|
195
|
+
404 => 'Not Found'
|
196
|
+
}[status] || 'Unknown'
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -4,35 +4,38 @@ module LazyGraph
|
|
4
4
|
# Module to provide lazy graph functionalities using stack pointers.
|
5
5
|
POINTER_POOL = []
|
6
6
|
|
7
|
-
StackPointer = Struct.new(:parent, :frame, :depth, :key, :root) do
|
7
|
+
StackPointer = Struct.new(:parent, :frame, :depth, :recursion_depth, :key, :root) do
|
8
8
|
attr_accessor :pointer_cache
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# Pushes a new frame onto the stack, creating or reusing a StackPointer.
|
11
|
+
# Frames represent activation contexts; keys are identifiers within those frames.
|
12
12
|
def push(frame, key)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
StackPointer.new(parent: self, frame: frame, key: key, depth: depth + 1, root: root || self)
|
23
|
-
end
|
13
|
+
ptr = POINTER_POOL.pop || StackPointer.new
|
14
|
+
ptr.parent = self
|
15
|
+
ptr.root = root || self
|
16
|
+
ptr.frame = frame
|
17
|
+
ptr.key = key
|
18
|
+
ptr.depth = depth + 1
|
19
|
+
ptr.recursion_depth = recursion_depth
|
20
|
+
ptr.pointer_cache&.clear
|
21
|
+
ptr
|
24
22
|
end
|
25
23
|
|
24
|
+
# Recycles the current StackPointer by adding it back to the pointer pool.
|
25
|
+
# Once recycled, this instance should no longer be used unless reassigned by push.
|
26
26
|
def recycle!
|
27
27
|
POINTER_POOL.push(self)
|
28
28
|
nil
|
29
29
|
end
|
30
30
|
|
31
|
+
# Retrieves the StackPointer at a specific index in the upward chain of parents.
|
31
32
|
def ptr_at(index)
|
32
33
|
@pointer_cache ||= {}.compare_by_identity
|
33
34
|
@pointer_cache[index] ||= depth == index ? self : parent&.ptr_at(index)
|
34
35
|
end
|
35
36
|
|
37
|
+
# Handles method calls not explicitly defined in this class by delegating them
|
38
|
+
# first to the frame, then to the parent, recursively up the stack.
|
36
39
|
def method_missing(name, *args, &block)
|
37
40
|
if frame.respond_to?(name)
|
38
41
|
frame.send(name, *args, &block)
|
@@ -43,20 +46,25 @@ module LazyGraph
|
|
43
46
|
end
|
44
47
|
end
|
45
48
|
|
49
|
+
# Returns the key associated with this stack pointer's frame.
|
46
50
|
def index
|
47
51
|
key
|
48
52
|
end
|
49
53
|
|
54
|
+
# Logs debugging information related to this stack pointer in the root frame's DEBUG section.
|
50
55
|
def log_debug(**log_item)
|
51
56
|
root.frame[:DEBUG] = [] if !root.frame[:DEBUG] || root.frame[:DEBUG].is_a?(MissingValue)
|
52
57
|
root.frame[:DEBUG] << { **log_item, location: to_s }
|
53
58
|
nil
|
54
59
|
end
|
55
60
|
|
61
|
+
# Determines if the stack pointer can respond to a missing method by mimicking the behavior
|
62
|
+
# of the frame or any parent stack pointers recursively.
|
56
63
|
def respond_to_missing?(name, include_private = false)
|
57
64
|
frame.respond_to?(name, include_private) || parent.respond_to?(name, include_private)
|
58
65
|
end
|
59
66
|
|
67
|
+
# Returns a string representation of the stacking path of keys up to this pointer.
|
60
68
|
def to_s
|
61
69
|
if parent
|
62
70
|
"#{parent}#{key.to_s =~ /\d+/ ? "[#{key}]" : ".#{key}"}"
|
data/lib/lazy_graph/version.rb
CHANGED
data/lib/lazy_graph.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'logger'
|
4
|
-
|
5
3
|
# LazyGraph is a library which allows you to define a strictly typed hierarchical Graph structure.
|
6
4
|
# Within this graph you can annotate certain nodes as derived nodes, which compute their outputs based
|
7
5
|
# on input dependencies on other nodes in the graph structure (can be relative and absolute references).
|
@@ -12,11 +10,7 @@ require 'logger'
|
|
12
10
|
#
|
13
11
|
module LazyGraph
|
14
12
|
class AbortError < StandardError; end
|
15
|
-
class
|
16
|
-
attr_accessor :logger
|
17
|
-
end
|
18
|
-
|
19
|
-
self.logger = Logger.new($stdout)
|
13
|
+
class ValidationError < StandardError; end
|
20
14
|
end
|
21
15
|
|
22
16
|
require_relative 'lazy_graph/context'
|
@@ -29,4 +23,8 @@ require_relative 'lazy_graph/hash_utils'
|
|
29
23
|
require_relative 'lazy_graph/builder'
|
30
24
|
require_relative 'lazy_graph/builder_group'
|
31
25
|
require_relative 'lazy_graph/stack_pointer'
|
32
|
-
require_relative 'lazy_graph/
|
26
|
+
require_relative 'lazy_graph/cli'
|
27
|
+
require_relative 'lazy_graph/rack_server'
|
28
|
+
require_relative 'lazy_graph/rack_app'
|
29
|
+
require_relative 'lazy_graph/logger'
|
30
|
+
require_relative 'lazy_graph/environment'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lazy_graph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 2025-01-07 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: json-schema
|
@@ -52,13 +52,13 @@ dependencies:
|
|
52
52
|
- !ruby/object:Gem::Version
|
53
53
|
version: '0'
|
54
54
|
- !ruby/object:Gem::Dependency
|
55
|
-
name:
|
55
|
+
name: benchmark-ips
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
57
57
|
requirements:
|
58
58
|
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '0'
|
61
|
-
type: :
|
61
|
+
type: :development
|
62
62
|
prerelease: false
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
@@ -66,7 +66,21 @@ dependencies:
|
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '0'
|
68
68
|
- !ruby/object:Gem::Dependency
|
69
|
-
name:
|
69
|
+
name: debug
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: fiddle
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
71
85
|
requirements:
|
72
86
|
- - ">="
|
@@ -80,7 +94,7 @@ dependencies:
|
|
80
94
|
- !ruby/object:Gem::Version
|
81
95
|
version: '0'
|
82
96
|
- !ruby/object:Gem::Dependency
|
83
|
-
name:
|
97
|
+
name: listen
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|
85
99
|
requirements:
|
86
100
|
- - ">="
|
@@ -94,7 +108,7 @@ dependencies:
|
|
94
108
|
- !ruby/object:Gem::Version
|
95
109
|
version: '0'
|
96
110
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
111
|
+
name: memory_profiler
|
98
112
|
requirement: !ruby/object:Gem::Requirement
|
99
113
|
requirements:
|
100
114
|
- - ">="
|
@@ -108,19 +122,61 @@ dependencies:
|
|
108
122
|
- !ruby/object:Gem::Version
|
109
123
|
version: '0'
|
110
124
|
- !ruby/object:Gem::Dependency
|
111
|
-
name:
|
125
|
+
name: observer
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
type: :development
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ">="
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: ostruct
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
141
|
+
requirements:
|
142
|
+
- - ">="
|
143
|
+
- !ruby/object:Gem::Version
|
144
|
+
version: '0'
|
145
|
+
type: :development
|
146
|
+
prerelease: false
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
- !ruby/object:Gem::Dependency
|
153
|
+
name: rack
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
type: :development
|
160
|
+
prerelease: false
|
161
|
+
version_requirements: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - ">="
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
- !ruby/object:Gem::Dependency
|
167
|
+
name: rake
|
112
168
|
requirement: !ruby/object:Gem::Requirement
|
113
169
|
requirements:
|
114
170
|
- - "~>"
|
115
171
|
- !ruby/object:Gem::Version
|
116
|
-
version: '
|
172
|
+
version: '13.0'
|
117
173
|
type: :development
|
118
174
|
prerelease: false
|
119
175
|
version_requirements: !ruby/object:Gem::Requirement
|
120
176
|
requirements:
|
121
177
|
- - "~>"
|
122
178
|
- !ruby/object:Gem::Version
|
123
|
-
version: '
|
179
|
+
version: '13.0'
|
124
180
|
- !ruby/object:Gem::Dependency
|
125
181
|
name: rdoc
|
126
182
|
requirement: !ruby/object:Gem::Requirement
|
@@ -135,6 +191,48 @@ dependencies:
|
|
135
191
|
- - ">="
|
136
192
|
- !ruby/object:Gem::Version
|
137
193
|
version: '0'
|
194
|
+
- !ruby/object:Gem::Dependency
|
195
|
+
name: rubocop
|
196
|
+
requirement: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
201
|
+
type: :development
|
202
|
+
prerelease: false
|
203
|
+
version_requirements: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - ">="
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0'
|
208
|
+
- !ruby/object:Gem::Dependency
|
209
|
+
name: solargraph
|
210
|
+
requirement: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - ">="
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0'
|
215
|
+
type: :development
|
216
|
+
prerelease: false
|
217
|
+
version_requirements: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: vernier
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
requirements:
|
226
|
+
- - ">="
|
227
|
+
- !ruby/object:Gem::Version
|
228
|
+
version: '0'
|
229
|
+
type: :development
|
230
|
+
prerelease: false
|
231
|
+
version_requirements: !ruby/object:Gem::Requirement
|
232
|
+
requirements:
|
233
|
+
- - ">="
|
234
|
+
- !ruby/object:Gem::Version
|
235
|
+
version: '0'
|
138
236
|
description: JSON Driven, Stateless Rules Engine for JIT and efficient evaluation
|
139
237
|
of complex rules and computation graphs.
|
140
238
|
email:
|
@@ -148,15 +246,20 @@ files:
|
|
148
246
|
- LICENSE.txt
|
149
247
|
- README.md
|
150
248
|
- Rakefile
|
249
|
+
- examples/converter.rb
|
151
250
|
- examples/performance_tests.rb
|
251
|
+
- examples/shopping_cart_totals.rb
|
152
252
|
- lib/lazy_graph.rb
|
153
253
|
- lib/lazy_graph/builder.rb
|
154
254
|
- lib/lazy_graph/builder/dsl.rb
|
155
255
|
- lib/lazy_graph/builder_group.rb
|
256
|
+
- lib/lazy_graph/cli.rb
|
156
257
|
- lib/lazy_graph/context.rb
|
258
|
+
- lib/lazy_graph/environment.rb
|
157
259
|
- lib/lazy_graph/graph.rb
|
158
260
|
- lib/lazy_graph/hash_utils.rb
|
159
261
|
- lib/lazy_graph/lazy-graph.json
|
262
|
+
- lib/lazy_graph/logger.rb
|
160
263
|
- lib/lazy_graph/missing_value.rb
|
161
264
|
- lib/lazy_graph/node.rb
|
162
265
|
- lib/lazy_graph/node/array_node.rb
|
@@ -168,7 +271,8 @@ files:
|
|
168
271
|
- lib/lazy_graph/path_parser/path.rb
|
169
272
|
- lib/lazy_graph/path_parser/path_group.rb
|
170
273
|
- lib/lazy_graph/path_parser/path_part.rb
|
171
|
-
- lib/lazy_graph/
|
274
|
+
- lib/lazy_graph/rack_app.rb
|
275
|
+
- lib/lazy_graph/rack_server.rb
|
172
276
|
- lib/lazy_graph/stack_pointer.rb
|
173
277
|
- lib/lazy_graph/version.rb
|
174
278
|
- logo.png
|