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