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: []
         
     |