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