Class: Filter

Inherits:
Object show all
Defined in:
lib/filter.rb

Overview

The Filter class is used to hold the information about the filter conditions that should be applied during certain Machinery commands.

Filters are usually created by passing a filter definition string to the constructor, e.g.

filter = Filter.new("/unmanaged_files/files/name=/opt")

Existing filters can be extended by amending the definition:

filter.add_element_filter_from_definition("/unmanaged_files/files/name=/srv")

or by adding ElementFilters directly:

element_filter = ElementFilter.new("/unmanaged_files/files/name", ["/opt", "/srv"])
filter.add_element_filter(element_filter)

The actual filtering can be done by passing values to Filter#matches?

filter = Filter.new("/unmanaged_files/files/name=/opt*")
filter.matches?("/unmanaged_files/files/name", "/opt/foo")
=> true
filter.matches?("/unmanaged_files/files/name", "/srv/bar")
=> false

More details about how the filter work can be found at github.com/SUSE/machinery/blob/master/docs/Filtering-Design.md

Constant Summary collapse

OPERATOR_EQUALS =
"="
OPERATOR_EQUALS_NOT =
"!="

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(definitions = []) ⇒ Filter

Returns a new instance of Filter.



88
89
90
# File 'lib/filter.rb', line 88

def initialize(definitions = [])
  @element_filters = Filter.parse_filter_definitions(definitions)
end

Instance Attribute Details

#element_filtersObject

Returns the value of attribute element_filters.



47
48
49
# File 'lib/filter.rb', line 47

def element_filters
  @element_filters
end

Class Method Details

.from_default_definition(command) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/filter.rb', line 72

def self.from_default_definition(command)
  filter = Filter.new

  default_filters_file = File.join(Machinery::ROOT, "filters", "default_filters.json")
  if File.exists?(default_filters_file)
    default_filters = JSON.parse(File.read(default_filters_file))
    if default_filters[command]
      default_filters[command].each do |definition|
        filter.add_element_filter_from_definition(definition)
      end
    end
  end

  filter
end

.parse_filter_definitions(filter_definitions) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/filter.rb', line 52

def self.parse_filter_definitions(filter_definitions)
  element_filters = {}
  Array(filter_definitions).each do |definition|
    path, operator, matcher_definition = definition.scan(/([a-zA-Z_\/]+)(.*=)(.*)/)[0]

    raise Machinery::Errors::InvalidFilter.new("Invalid filter: '#{definition}'") if !operator
    element_filters[path] ||= ElementFilter.new(path)
    if matcher_definition.index(",")
      matchers = matcher_definition.split(/(?<!\\),/)
      matchers.map! { |matcher| matcher.gsub("\\,", ",") } # Unescape escaped commas

      element_filters[path].add_matchers(operator, [matchers])
    else
      element_filters[path].add_matchers(operator, matcher_definition)
    end
  end

  element_filters
end

Instance Method Details

#add_element_filter_from_definition(filter_definition) ⇒ Object



92
93
94
# File 'lib/filter.rb', line 92

def add_element_filter_from_definition(filter_definition)
  add_element_filters(Filter.parse_filter_definitions(filter_definition).values)
end

#add_element_filters(element_filters) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/filter.rb', line 96

def add_element_filters(element_filters)
  Array(element_filters).each do |element_filter|
    path = element_filter.path
    @element_filters[path] ||= ElementFilter.new(path)

    element_filter.matchers.each do |operator, matchers|
      @element_filters[path].add_matchers(operator, matchers)
    end
  end
end

#apply!(system_description) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/filter.rb', line 142

def apply!(system_description)
  element_filters.each do |path, element_filter|
    steps = path.split("/").reject(&:empty?)
    target = steps.pop

    pointer = system_description
    container = nil
    steps.each do |step|
      break if !pointer
      pointer = pointer[step]
      container ||= pointer if pointer.is_a?(Machinery::Array)
    end

    next if !pointer

    begin
      pointer.delete_if do |element|
        element_filter.matches?(element[target])
      end
    rescue Machinery::Errors::ElementFilterTypeMismatch => e
      Machinery::Ui.warn("Warning: Filter '#{e.failed_matcher}' tries to match an array, " \
        "but the according element is not an array.")
    end
  end
end

#element_filter_for(path) ⇒ Object



107
108
109
# File 'lib/filter.rb', line 107

def element_filter_for(path)
  element_filters[path]
end

#element_filters_for_scope(scope) ⇒ Object



111
112
113
114
115
# File 'lib/filter.rb', line 111

def element_filters_for_scope(scope)
  @element_filters.values.select do |element_filter|
    element_filter.filters_scope?(scope)
  end
end

#empty?Boolean

Returns:

  • (Boolean)


168
169
170
# File 'lib/filter.rb', line 168

def empty?
  element_filters.empty?
end

#matches?(path, value) ⇒ Boolean

Returns:

  • (Boolean)


135
136
137
138
139
140
# File 'lib/filter.rb', line 135

def matches?(path, value)
  filter = element_filter_for(path)
  return false if !filter

  filter.matches?(value)
end

#set_element_filters_for_scope(scope, element_filters) ⇒ Object



117
118
119
120
121
122
123
# File 'lib/filter.rb', line 117

def set_element_filters_for_scope(scope, element_filters)
  @element_filters.reject! do |_path, element_filter|
    element_filter.filters_scope?(scope)
  end

  add_element_filters(element_filters)
end

#to_arrayObject



125
126
127
128
129
130
131
132
133
# File 'lib/filter.rb', line 125

def to_array
  @element_filters.flat_map do |path, element_filter|
    element_filter.matchers.flat_map do |operator, matchers|
      matchers.map do |matcher|
        "#{path}#{operator}#{Array(matcher).join(",")}"
      end
    end
  end
end