140 lines
3.5 KiB
Ruby
140 lines
3.5 KiB
Ruby
class FilterProcessor
|
|
|
|
class InvalidFilter < ::ArgumentError
|
|
end
|
|
|
|
def process_all(filters, email)
|
|
filters.each { |filter|
|
|
email = process(filter, email)
|
|
}
|
|
|
|
email
|
|
end
|
|
|
|
def process(filter, email)
|
|
return email unless filter.enabled?
|
|
|
|
if evaluate_conditions(filter, email)
|
|
email = execute_operations(filter.operations, email)
|
|
end
|
|
|
|
email
|
|
end
|
|
|
|
def evaluate_conditions(filter, email)
|
|
filter_result = true
|
|
|
|
filter_result = catch(:done) {
|
|
filter.conditions.each do |condition|
|
|
next unless condition.enabled?
|
|
|
|
properties = prepare_properties(condition, email)
|
|
condition_result = apply_condition(properties, condition)
|
|
condition_result = !condition_result if filter.inverted?
|
|
filter_result = apply_operator(filter_result, filter.operator, condition_result)
|
|
rescue InvalidFilter => ex
|
|
Rails.logger.error "Skipped filter##{filter.id} '#{filter.description}' - #{ex.inspect}"
|
|
next
|
|
end
|
|
}
|
|
|
|
filter_result
|
|
end
|
|
|
|
def execute_operations(operations, email)
|
|
operations.each do |operation|
|
|
next unless operation.enabled?
|
|
|
|
klass = operation.class_name.constantize
|
|
email_operation = klass.new(operation: operation)
|
|
email = email_operation.process(email)
|
|
rescue NameError => ex
|
|
Rails.logger.error "Skipped operation##{operation.id} '#{operation.class_name}' - #{ex.inspect}"
|
|
raise InvalidFilter, ex.inspect
|
|
end
|
|
|
|
email
|
|
end
|
|
|
|
def prepare_properties(filter, email)
|
|
case filter.property_type.downcase
|
|
when "header"
|
|
Array(email.header_values(filter.property_value))
|
|
when "subject"
|
|
Array(email.subject)
|
|
when "body"
|
|
Array(email.plain_body)
|
|
else
|
|
raise InvalidFilter, "Unrecognized property type '#{filter.property_type}'"
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def apply_condition(properties, condition)
|
|
case condition.test_method.downcase
|
|
when "match", "matches"
|
|
properties.any? { |property|
|
|
pattern = Regexp.new(condition.test_value)
|
|
property.match? pattern
|
|
}
|
|
when "equal", "equals"
|
|
properties.any? { |property|
|
|
property == condition.test_value
|
|
}
|
|
when "start", "starts"
|
|
properties.any? { |property|
|
|
property.starts_with? condition.test_value
|
|
}
|
|
when "end", "ends"
|
|
properties.any? { |property|
|
|
property.ends_with? condition.test_value
|
|
}
|
|
when "contain", "contains"
|
|
properties.any? { |property|
|
|
property.include? condition.test_value
|
|
}
|
|
when "exist", "exists"
|
|
properties.any? { |property|
|
|
property.exists?
|
|
}
|
|
when "empty"
|
|
properties.all? { |property|
|
|
property.empty?
|
|
}
|
|
when "date_before"
|
|
# properties.all? { |property|
|
|
# property.empty?
|
|
# }
|
|
when "date_after"
|
|
# properties.all? { |property|
|
|
# property.empty?
|
|
# }
|
|
else
|
|
raise InvalidFilter, "Unrecognized test method '#{condition.test_method}'"
|
|
end
|
|
end
|
|
|
|
def apply_operator(state, operator, result)
|
|
case operator.upcase
|
|
when "AND"
|
|
if result
|
|
(state and result)
|
|
else
|
|
throw :done, false
|
|
end
|
|
when "OR"
|
|
if result
|
|
throw :done, true
|
|
else
|
|
(state or result)
|
|
end
|
|
# when "XOR"
|
|
# (state or result) and !(conditions_state and result)
|
|
else
|
|
raise InvalidFilter, "Unrecognized operator '#{operator}'"
|
|
end
|
|
end
|
|
|
|
end
|