class Optimizely::CustomAttributeConditionEvaluator

Constants

CUSTOM_ATTRIBUTE_CONDITION_TYPE
EVALUATORS_BY_MATCH_TYPE
EXACT_MATCH_TYPE

Conditional match types

EXISTS_MATCH_TYPE
GREATER_EQUAL_MATCH_TYPE
GREATER_THAN_MATCH_TYPE
LESS_EQUAL_MATCH_TYPE
LESS_THAN_MATCH_TYPE
SEMVER_EQ
SEMVER_GE
SEMVER_GT
SEMVER_LE
SEMVER_LT
SUBSTRING_MATCH_TYPE

Attributes

user_attributes[R]

Public Class Methods

new(user_attributes, logger) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 58
def initialize(user_attributes, logger)
  @user_attributes = user_attributes
  @logger = logger
end

Public Instance Methods

evaluate(leaf_condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 63
def evaluate(leaf_condition)
  # Top level method to evaluate audience conditions.
  #
  # conditions - Nested array of and/or conditions.
  #              Example: ['and', operand_1, ['or', operand_2, operand_3]]
  #
  # Returns boolean if the given user attributes match/don't match the given conditions,
  #         nil if the given conditions can't be evaluated.

  unless leaf_condition['type'] == CUSTOM_ATTRIBUTE_CONDITION_TYPE
    @logger.log(
      Logger::WARN,
      format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_TYPE'], leaf_condition)
    )
    return nil
  end

  condition_match = leaf_condition['match'] || EXACT_MATCH_TYPE

  if !@user_attributes.key?(leaf_condition['name']) && condition_match != EXISTS_MATCH_TYPE
    @logger.log(
      Logger::DEBUG,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['MISSING_ATTRIBUTE_VALUE'],
        leaf_condition,
        leaf_condition['name']
      )
    )
    return nil
  end

  if @user_attributes[leaf_condition['name']].nil? && condition_match != EXISTS_MATCH_TYPE
    @logger.log(
      Logger::DEBUG,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['NULL_ATTRIBUTE_VALUE'],
        leaf_condition,
        leaf_condition['name']
      )
    )
    return nil
  end

  unless EVALUATORS_BY_MATCH_TYPE.include?(condition_match)
    @logger.log(
      Logger::WARN,
      format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_MATCH_TYPE'], leaf_condition)
    )
    return nil
  end

  begin
    send(EVALUATORS_BY_MATCH_TYPE[condition_match], leaf_condition)
  rescue InvalidAttributeType
    condition_name = leaf_condition['name']
    user_value = @user_attributes[condition_name]

    @logger.log(
      Logger::WARN,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNEXPECTED_TYPE'],
        leaf_condition,
        user_value.class,
        condition_name
      )
    )
    return nil
  rescue InvalidSemanticVersion
    condition_name = leaf_condition['name']

    @logger.log(
      Logger::WARN,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INVALID_SEMANTIC_VERSION'],
        leaf_condition,
        condition_name
      )
    )
    return nil
  end
end
exact_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 145
def exact_evaluator(condition)
  # Evaluate the given exact match condition for the given user attributes.
  #
  # Returns boolean true if numbers values matched, i.e 2 is equal to 2.0
  #                 true if the user attribute value is equal (===) to the condition value,
  #                 false if the user attribute value is not equal (!==) to the condition value,
  #                 nil if the condition value or user attribute value has an invalid type,
  #                 or if there is a mismatch between the user attribute type and the condition value type.

  condition_value = condition['value']

  user_provided_value = @user_attributes[condition['name']]

  if !value_type_valid_for_exact_conditions?(condition_value) ||
     (condition_value.is_a?(Numeric) && !Helpers::Validator.finite_number?(condition_value))
    @logger.log(
      Logger::WARN,
      format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
    )
    return nil
  end

  if !value_type_valid_for_exact_conditions?(user_provided_value) ||
     !Helpers::Validator.same_types?(condition_value, user_provided_value)
    raise InvalidAttributeType
  end

  if user_provided_value.is_a?(Numeric) && !Helpers::Validator.finite_number?(user_provided_value)
    @logger.log(
      Logger::WARN,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INFINITE_ATTRIBUTE_VALUE'],
        condition,
        condition['name']
      )
    )
    return nil
  end

  condition_value == user_provided_value
end
exists_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 187
def exists_evaluator(condition)
  # Evaluate the given exists match condition for the given user attributes.
  # Returns boolean true if both:
  #                    1) the user attributes have a value for the given condition, and
  #                    2) the user attribute value is neither nil nor undefined
  #                 Returns false otherwise

  !@user_attributes[condition['name']].nil?
end
greater_than_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 197
def greater_than_evaluator(condition)
  # Evaluate the given greater than match condition for the given user attributes.
  # Returns boolean true if the user attribute value is greater than the condition value,
  #                 false if the user attribute value is less than or equal to the condition value,
  #                 nil if the condition value isn't a number or the user attribute value isn't a number.

  condition_value = condition['value']
  user_provided_value = @user_attributes[condition['name']]

  return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)

  user_provided_value > condition_value
end
greater_than_or_equal_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 211
def greater_than_or_equal_evaluator(condition)
  # Evaluate the given greater than or equal match condition for the given user attributes.
  # Returns boolean true if the user attribute value is greater than or equal to the condition value,
  #                 false if the user attribute value is less than the condition value,
  #                 nil if the condition value isn't a number or the user attribute value isn't a number.

  condition_value = condition['value']
  user_provided_value = @user_attributes[condition['name']]

  return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)

  user_provided_value >= condition_value
end
less_than_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 225
def less_than_evaluator(condition)
  # Evaluate the given less than match condition for the given user attributes.
  # Returns boolean true if the user attribute value is less than the condition value,
  #                 false if the user attribute value is greater than or equal to the condition value,
  #                 nil if the condition value isn't a number or the user attribute value isn't a number.

  condition_value = condition['value']
  user_provided_value = @user_attributes[condition['name']]

  return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)

  user_provided_value < condition_value
end
less_than_or_equal_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 239
def less_than_or_equal_evaluator(condition)
  # Evaluate the given less than or equal match condition for the given user attributes.
  # Returns boolean true if the user attribute value is less than or equal to the condition value,
  #                 false if the user attribute value is greater than the condition value,
  #                 nil if the condition value isn't a number or the user attribute value isn't a number.

  condition_value = condition['value']
  user_provided_value = @user_attributes[condition['name']]

  return nil unless valid_numeric_values?(user_provided_value, condition_value, condition)

  user_provided_value <= condition_value
end
semver_equal_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 275
def semver_equal_evaluator(condition)
  # Evaluate the given semantic version equal match target version for the user version.
  # Returns boolean true if the user version is equal to the target version,
  #                 false if the user version is not equal to the target version

  target_version = condition['value']
  user_version = @user_attributes[condition['name']]

  SemanticVersion.compare_user_version_with_target_version(target_version, user_version).zero?
end
semver_greater_than_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 286
def semver_greater_than_evaluator(condition)
  # Evaluate the given semantic version greater than match target version for the user version.
  # Returns boolean true if the user version is greater than the target version,
  #                 false if the user version is less than or equal to the target version

  target_version = condition['value']
  user_version = @user_attributes[condition['name']]

  SemanticVersion.compare_user_version_with_target_version(target_version, user_version).positive?
end
semver_greater_than_or_equal_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 297
def semver_greater_than_or_equal_evaluator(condition)
  # Evaluate the given semantic version greater than or equal to match target version for the user version.
  # Returns boolean true if the user version is greater than or equal to the target version,
  #                 false if the user version is less than the target version

  target_version = condition['value']
  user_version = @user_attributes[condition['name']]

  SemanticVersion.compare_user_version_with_target_version(target_version, user_version) >= 0
end
semver_less_than_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 308
def semver_less_than_evaluator(condition)
  # Evaluate the given semantic version less than match target version for the user version.
  # Returns boolean true if the user version is less than the target version,
  #                 false if the user version is greater than or equal to the target version

  target_version = condition['value']
  user_version = @user_attributes[condition['name']]

  SemanticVersion.compare_user_version_with_target_version(target_version, user_version).negative?
end
semver_less_than_or_equal_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 319
def semver_less_than_or_equal_evaluator(condition)
  # Evaluate the given semantic version less than or equal to match target version for the user version.
  # Returns boolean true if the user version is less than or equal to the target version,
  #                 false if the user version is greater than the target version

  target_version = condition['value']
  user_version = @user_attributes[condition['name']]

  SemanticVersion.compare_user_version_with_target_version(target_version, user_version) <= 0
end
substring_evaluator(condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 253
def substring_evaluator(condition)
  # Evaluate the given substring match condition for the given user attributes.
  # Returns boolean true if the condition value is a substring of the user attribute value,
  #                 false if the condition value is not a substring of the user attribute value,
  #                 nil if the condition value isn't a string or the user attribute value isn't a string.

  condition_value = condition['value']
  user_provided_value = @user_attributes[condition['name']]

  unless condition_value.is_a?(String)
    @logger.log(
      Logger::WARN,
      format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
    )
    return nil
  end

  raise InvalidAttributeType unless user_provided_value.is_a?(String)

  user_provided_value.include? condition_value
end

Private Instance Methods

valid_numeric_values?(user_value, condition_value, condition) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 332
def valid_numeric_values?(user_value, condition_value, condition)
  # Returns true if user and condition values are valid numeric.
  #         false otherwise.

  unless Helpers::Validator.finite_number?(condition_value)
    @logger.log(
      Logger::WARN,
      format(Helpers::Constants::AUDIENCE_EVALUATION_LOGS['UNKNOWN_CONDITION_VALUE'], condition)
    )
    return false
  end

  raise InvalidAttributeType unless user_value.is_a?(Numeric)

  unless Helpers::Validator.finite_number?(user_value)
    @logger.log(
      Logger::WARN,
      format(
        Helpers::Constants::AUDIENCE_EVALUATION_LOGS['INFINITE_ATTRIBUTE_VALUE'],
        condition,
        condition['name']
      )
    )
    return false
  end

  true
end
value_type_valid_for_exact_conditions?(value) click to toggle source
# File lib/optimizely/custom_attribute_condition_evaluator.rb, line 361
def value_type_valid_for_exact_conditions?(value)
  # Returns true if the value is valid for exact conditions. Valid values include
  #  strings or booleans or is a number.
  #  false otherwise.

  (Helpers::Validator.boolean? value) || (value.is_a? String) || value.is_a?(Numeric)
end