Class: CML::LogicTree::Graph
- Inherits:
-
Object
- Object
- CML::LogicTree::Graph
- Defined in:
- lib/cml/logic_tree/graph.rb
Overview
Generates a DAG, directed acyclic graph of the logic tree.
Defined Under Namespace
Classes: Error, UnresolvableDependency
Instance Attribute Summary collapse
-
#logic_tree ⇒ Object
Returns the value of attribute logic_tree.
-
#structure ⇒ Object
Returns the value of attribute structure.
Instance Method Summary collapse
-
#create_structure ⇒ Object
Generate a hash of field names with hashes of properties, e.g.: { “omg”=>{ :outbound_count=>1, :outbound_names=> }, “w_t_f”=>{ :inbound_count=>1, :inbound_names=>, :inbound_matches=>=> [{“omg”=>[{:match_key => nil, :is_not=>true]}]} } }.
-
#initialize(logic_tree) ⇒ Graph
constructor
A new instance of Graph.
-
#map_dependencies ⇒ Object
Returns a hash of the logic tree’s nodes.
Constructor Details
#initialize(logic_tree) ⇒ Graph
Returns a new instance of Graph.
14 15 16 17 18 |
# File 'lib/cml/logic_tree/graph.rb', line 14 def initialize(logic_tree) raise(ArgumentError, "Requires a CML::LogicTree, instead got #{logic_tree.inspect}") unless CML::LogicTree===logic_tree @logic_tree = logic_tree create_structure end |
Instance Attribute Details
#logic_tree ⇒ Object
Returns the value of attribute logic_tree.
12 13 14 |
# File 'lib/cml/logic_tree/graph.rb', line 12 def logic_tree @logic_tree end |
#structure ⇒ Object
Returns the value of attribute structure.
12 13 14 |
# File 'lib/cml/logic_tree/graph.rb', line 12 def structure @structure end |
Instance Method Details
#create_structure ⇒ Object
Generate a hash of field names with hashes of properties, e.g.:
{
"omg"=>{
:outbound_count=>1, :outbound_names=>["w_t_f"] },
"w_t_f"=>{
:inbound_count=>1, :inbound_names=>["omg"], :inbound_matches=>{"||" => [{"omg"=>[{:match_key => nil, :is_not=>true}]}]} }
}
For inbound matches, the match key tests the value of the remote vertex. Boolean combinators are represented here.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/cml/logic_tree/graph.rb', line 30 def create_structure # First, inbound links. @structure = map_dependencies do |field, cml_tag, dependency_names, dependencies| if dependencies dependency_names.uniq! { :inbound_count => dependency_names.size, :inbound_names => dependency_names, :inbound_matches => dependencies } else {} end end # Map outbound links by iterating over each node's inbound. @structure.each do |vertex, props| next if props.nil? || props[:inbound_names].nil? props[:inbound_names].each do |inbound_name| x = @structure[inbound_name] c, n = x[:outbound_count], x[:outbound_names] x[:outbound_names] = (n ? n<<vertex : [vertex]).sort.uniq x[:outbound_count] = x[:outbound_names].size end end @structure end |
#map_dependencies ⇒ Object
Returns a hash of the logic tree’s nodes.
When a block is given, each node’s entry is set to the return value of the block.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/cml/logic_tree/graph.rb', line 57 def map_dependencies structure = {} deferred_vertex_procs = [] deferred_vertex_counts = Hash.new(0) created_vertex_for_fields = [] @logic_tree.nodes.select {|k,v| v.tag.in_logic_graph? }.each do |field, cml_tag| # This proc is used to generate each vertex. map_vertex = lambda do |cml_tag, dependency_names, dependencies| if block_given? structure[field] = yield(field, cml_tag, dependency_names, dependencies) else structure[field] = { :cml_tag => cml_tag, :dependency_names => dependency_names, :dependencies => dependencies } end created_vertex_for_fields << field end # Expand dependencies & also collect a simple list of dependency names. dependency_names = [] dependencies = cml_tag.dependencies_on {|name, descs| dependency_names << name } # Defer when a CML tag has unsatisfied dependencies, or just generate its vertex. if dependency_names.empty? map_vertex.call(cml_tag, nil, nil) else deferred_vertex_procs << lambda do |this_proc| if dependency_names.all? {|name| created_vertex_for_fields.include? name } map_vertex.call(cml_tag, dependency_names, dependencies) else # Push onto bottom of stack to try again last. raise(UnresolvableDependency, "CML element '#{field}' contains invalid only-if logic.") if deferred_vertex_counts[field] > DEPTH_LIMIT deferred_vertex_counts[field] += 1 deferred_vertex_procs.unshift(this_proc) end end end end # Iterate through deferred dependencies, filling out the structure as dependencies are satisfied. while !deferred_vertex_procs.empty? do p = deferred_vertex_procs.pop p.call(p) # pass the Proc itself, in case it needs to defer again end structure end |