class Jason::GraphHelper

Attributes

id[R]
includes_helper[R]

Public Class Methods

new(id, includes_helper) click to toggle source
# File lib/jason/graph_helper.rb, line 4
def initialize(id, includes_helper)
  @id = id
  @includes_helper = includes_helper
end

Public Instance Methods

add_edge(parent_model, parent_id, child_model, child_id) click to toggle source
# File lib/jason/graph_helper.rb, line 9
def add_edge(parent_model, parent_id, child_model, child_id)
  edge = "#{parent_model}:#{parent_id}/#{child_model}:#{child_id}"
  $redis_jason.sadd("jason:subscriptions:#{id}:graph", edge)
end
add_edges(all_models, instance_ids) click to toggle source
# File lib/jason/graph_helper.rb, line 19
def add_edges(all_models, instance_ids)
  edges = build_edges(all_models, instance_ids)
  $redis_jason.sadd("jason:subscriptions:#{id}:graph", edges)
end
apply_add_node_at_root(node) click to toggle source
# File lib/jason/graph_helper.rb, line 29
def apply_add_node_at_root(node)
  diff_edges_from_graph(add_edges: ["root/#{node}"])
end
apply_remove_node(node) click to toggle source
# File lib/jason/graph_helper.rb, line 33
def apply_remove_node(node)
  edges = $redis_jason.smembers("jason:subscriptions:#{id}:graph")
  edges = find_edges_with_node(edges, node)
  diff_edges_from_graph(remove_edges: edges)
end
apply_update(add: nil, remove: nil, enforce: false) click to toggle source

Add and remove edges, return graph before and after Enforce means make the graph contain only the add_edges

# File lib/jason/graph_helper.rb, line 41
def apply_update(add: nil, remove: nil, enforce: false)
  add_edges = []
  remove_edges = []

  if add.present?
    add.each do |edge_set|
      add_edges += build_edges(edge_set[:model_names], edge_set[:instance_ids])
    end
  end

  if remove.present?
    remove.each do |edge_set|
      remove_edges += build_edges(edge_set[:model_names], edge_set[:instance_ids], include_root: false)
    end
  end

  diff_edges_from_graph(add_edges: add_edges, remove_edges: remove_edges, enforce: enforce)
end
build_graph_from_edges(edges) click to toggle source
# File lib/jason/graph_helper.rb, line 135
def build_graph_from_edges(edges)
  graph = {}
  edges.each do |edge|
    parent, child = edge.split('/')
    graph[parent] ||= []
    graph[parent].push(child)
  end
  graph
end
diff_edges_from_graph(add_edges: [], remove_edges: [], enforce: false) click to toggle source
# File lib/jason/graph_helper.rb, line 60
def diff_edges_from_graph(add_edges: [], remove_edges: [], enforce: false)
  if enforce
    old_edges = $redis_jason.multi do |r|
      r.smembers("jason:subscriptions:#{id}:graph")
      r.del("jason:subscriptions:#{id}:graph")
      r.sadd("jason:subscriptions:#{id}:graph", add_edges) if add_edges.present?
    end[0]
    new_edges = add_edges
  else
    old_edges, new_edges = Jason::LuaGenerator.new.update_set_with_diff("jason:subscriptions:#{id}:graph", add_edges.flatten, remove_edges.flatten)
  end

  old_graph = build_graph_from_edges(old_edges)
  new_graph = build_graph_from_edges(new_edges)

  old_nodes = (old_graph.values + old_graph.keys).flatten.uniq - ['root']
  new_nodes = (new_graph.values + new_graph.keys).flatten.uniq - ['root']
  orphan_nodes = find_orphans_in_graph(new_graph)

  added_nodes = new_nodes - old_nodes - orphan_nodes
  removed_nodes = old_nodes - new_nodes + orphan_nodes

  orphaned_edges = orphan_nodes.map do |node|
    find_edges_with_node(new_edges, node)
  end.flatten

  if orphaned_edges.present?
    $redis_jason.srem("jason:subscriptions:#{id}:graph", orphaned_edges)
  end

  ids_to_add = {}
  ids_to_remove = {}

  added_nodes.each do |node|
    model_name, instance_id = node.split(':')
    ids_to_add[model_name] ||= []
    ids_to_add[model_name].push(instance_id)
  end

  removed_nodes.each do |node|
    model_name, instance_id = node.split(':')
    ids_to_remove[model_name] ||= []
    ids_to_remove[model_name].push(instance_id)
  end

  { ids_to_remove: ids_to_remove, ids_to_add: ids_to_add }
end
find_edges_with_node(edges, node) click to toggle source
# File lib/jason/graph_helper.rb, line 108
def find_edges_with_node(edges, node)
  edges.select do |edge|
    parent, child = edge.split('/')
    parent == node || child == node
  end
end
find_orphans() click to toggle source
# File lib/jason/graph_helper.rb, line 115
def find_orphans
  edges = $redis_jason.smembers("jason:subscriptions:#{id}:graph")
  graph = build_graph_from_edges(edges)
  find_orphans_in_graph(graph)
end
find_orphans_in_graph(graph) click to toggle source
# File lib/jason/graph_helper.rb, line 121
def find_orphans_in_graph(graph)
  reachable_nodes = get_reachable_nodes(graph)
  all_nodes = (graph.values + graph.keys).flatten.uniq - ['root']
  all_nodes - reachable_nodes
end
get_reachable_nodes(graph, parent = 'root') click to toggle source
# File lib/jason/graph_helper.rb, line 127
def get_reachable_nodes(graph, parent = 'root')
  reached_nodes = graph[parent] || []
  reached_nodes.each do |child|
    reached_nodes += get_reachable_nodes(graph, child)
  end
  reached_nodes
end
remove_edge(parent_model, parent_id, child_model, child_id) click to toggle source
# File lib/jason/graph_helper.rb, line 14
def remove_edge(parent_model, parent_id, child_model, child_id)
  edge = "#{parent_model}:#{parent_id}/#{child_model}:#{child_id}"
  $redis_jason.srem("jason:subscriptions:#{id}:graph", edge)
end
remove_edges(all_models, instance_ids) click to toggle source
# File lib/jason/graph_helper.rb, line 24
def remove_edges(all_models, instance_ids)
  edges = build_edges(all_models, instance_ids)
  $redis_jason.srem("jason:subscriptions:#{id}:graph", edges)
end

Private Instance Methods

build_edges(all_models, instance_ids, include_root: true) click to toggle source
# File lib/jason/graph_helper.rb, line 147
def build_edges(all_models, instance_ids, include_root: true)
  # Build the tree
  # Find parent -> child model relationships
  edges = []

  all_models.each_with_index do |parent_model, parent_idx|
    all_models.each_with_index do |child_model, child_idx|
      next if parent_model == child_model
      next if !includes_helper.in_sub(parent_model, child_model)

      pairs = instance_ids.map { |row| [row[parent_idx], row[child_idx]] }
        .uniq
        .reject{ |pair| pair[0].blank? || pair[1].blank? }

      edges += pairs.map.each do |pair|
        "#{parent_model}:#{pair[0]}/#{child_model}:#{pair[1]}"
      end
    end
  end

  root_model = includes_helper.root_model

  if include_root && all_models.include?(root_model)
    root_idx = all_models.find_index(root_model)
    root_ids = instance_ids.map { |row| row[root_idx] }.uniq.compact

    edges += root_ids.map do |id|
      "root/#{root_model}:#{id}"
    end
  end

  edges
end