Module: Metasploit::Model::Association::Tree

Defined in:
lib/metasploit/model/association/tree.rb

Overview

Functions for turning a compact tree of compact as passed to Search::Association::ClassMethods#search_associations into an expanded Search::Association::ClassMethods#search_association_tree.

Class Method Summary collapse

Class Method Details

.expand(compact) ⇒ Hash{Symbol => Hash,nil}

Expands a compact association into an expanded association tree.

Parameters:

Returns:

  • (Hash{Symbol => Hash,nil})

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/metasploit/model/association/tree.rb', line 10

def self.expand(compact)
  case compact
    when Array
      compact.reduce({}) { |hash, association|
        hash.merge(expand(association))
      }
    when Hash
      child_by_parent = compact

      child_by_parent.each_with_object({}) { |(parent, child), hash|
        hash[parent] = expand(child)
      }
    when Symbol
      association = compact

      {association => nil}
  end
end

.merge(first_expanded, second_expanded) ⇒ nil, Hash{Symbol => nil,Hash}

Note:

Unlike Hash#deep_merge, second_expanded's values aren't favored over first's values. Instead whichever side is present is used and if both first and second_expanded are present, then their Hash#keys' values are recursively merged.

Merges two expanded association trees.

Parameters:

  • first_expanded (nil, Hash{Symbol => nil,Hash})

    An expanded association tree as from expand

  • second_expanded (nil, Hash{Symbol => nil,Hash})

    An expanded association tree as from expand

Returns:

  • (nil, Hash{Symbol => nil,Hash})

    a new expanded association tree.


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/metasploit/model/association/tree.rb', line 38

def self.merge(first_expanded, second_expanded)
  if first_expanded.nil? && second_expanded.nil?
    nil
  elsif !first_expanded.nil? && second_expanded.nil?
    first_expanded
  elsif first_expanded.nil? && !second_expanded.nil?
    second_expanded
  else
    first_keys = first_expanded.keys
    key_set = Set.new(first_keys)

    second_keys = second_expanded.keys
    key_set.merge(second_keys)

    key_set.each_with_object({}) do |key, merged|
      first_child = first_expanded[key]
      second_child = second_expanded[key]

      merged[key] = merge(first_child, second_child)
    end
  end
end

.operators(expanded, options = {}) ⇒ Array<Metasploit::Model::Search::Operator::Association>

Calculates association operators for the expanded association tree.

Parameters:

  • expanded (Hash{Symbol => Hash,nil}, nil)

    An expanded association tree.

  • options (Hash{Symbol => Class}) (defaults to: {})

Options Hash (options):

  • :class (Class, #reflect_on_association)

    The Class on which the top-level key associations in expanded are declared.

Returns:


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
# File 'lib/metasploit/model/association/tree.rb', line 68

def self.operators(expanded, options={})
  expanded ||= {}

  options.assert_valid_keys(:class)
  klass = options.fetch(:class)

  expanded.flat_map { |parent_association, child_tree|
    reflection = reflect_on_association_on_class(parent_association, klass)
    association_class = reflection.klass

    association_search_with_operators = association_class.search_with_operator_by_name.each_value

    child_tree_operators = operators(
        child_tree,
        class: reflection.klass
    )

    [association_search_with_operators, child_tree_operators].flat_map { |enumerator|
      enumerator.map { |source_operator|
        Metasploit::Model::Search::Operator::Association.new(
            association: parent_association,
            klass: klass,
            source_operator: source_operator
        )
      }
    }
  }
end

.reflect_on_association_on_class(association, klass) ⇒ #klass (private)

Return the association reflection for association on klass.

Parameters:

  • association (Symbol)

    name of an association on klass.

  • klass (#reflect_on_association)

    Class on which association is declared.

Returns:

  • (#klass)

    Association reflection that can give the #klass pointed to by the association.

Raises:


106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/metasploit/model/association/tree.rb', line 106

def self.reflect_on_association_on_class(association, klass)
  begin
    reflection = klass.reflect_on_association(association)
  rescue NameError
    raise NameError,
          "#{self} does not respond to reflect_on_association.  " \
                    "It can be added to ActiveModels by including Metasploit::Model::Association into the class."
  end

  unless reflection
    raise Metasploit::Model::Association::Error.new(
              model: klass,
              name: association
          )
  end

  reflection
end