class ConfigCat::RolloutEvaluator
Constants
- COMPARATOR_TEXTS
Public Class Methods
evaluate(key, user, default_value, default_variation_id, config)
click to toggle source
# File lib/configcat/rolloutevaluator.rb, line 10 def self.evaluate(key, user, default_value, default_variation_id, config) ConfigCat.logger.info("Evaluating get_value('%s')." % key) feature_flags = config.fetch(FEATURE_FLAGS, nil) if feature_flags === nil ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s' Returning default_value: [%s]." % [key, key, default_value.to_s]) return default_value, default_variation_id end setting_descriptor = feature_flags.fetch(key, nil) if setting_descriptor === nil ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s'. Returning default_value: [%s]. Here are the available keys: %s" % [key, key, default_value.to_s, feature_flags.keys.join(", ")]) return default_value, default_variation_id end rollout_rules = setting_descriptor.fetch(ROLLOUT_RULES, []) rollout_percentage_items = setting_descriptor.fetch(ROLLOUT_PERCENTAGE_ITEMS, []) if !user.equal?(nil) && !user.class.equal?(User) ConfigCat.logger.warn("Evaluating get_value('%s'). User Object is not an instance of User type." % key) user = nil end if user === nil if rollout_rules.size > 0 || rollout_percentage_items.size > 0 ConfigCat.logger.warn("Evaluating get_value('%s'). UserObject missing! You should pass a UserObject to get_value(), in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/" % key) end return_value = setting_descriptor.fetch(VALUE, default_value) return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id) ConfigCat.logger.info("Returning [%s]" % return_value.to_s) return return_value, return_variation_id end ConfigCat.logger.info("User object:\n%s" % user.to_s) # Evaluate targeting rules for rollout_rule in rollout_rules comparison_attribute = rollout_rule.fetch(COMPARISON_ATTRIBUTE) comparison_value = rollout_rule.fetch(COMPARISON_VALUE, nil) comparator = rollout_rule.fetch(COMPARATOR, nil) user_value = user.get_attribute(comparison_attribute) if user_value === nil || !user_value ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value)) next end value = rollout_rule.fetch(VALUE, nil) variation_id = rollout_rule.fetch(VARIATION_ID, default_variation_id) # IS ONE OF if comparator == 0 if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end # IS NOT ONE OF elsif comparator == 1 if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end # CONTAINS elsif comparator == 2 if user_value.to_s.include?(comparison_value.to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end # DOES NOT CONTAIN elsif comparator == 3 if !user_value.to_s.include?(comparison_value.to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end # IS ONE OF, IS NOT ONE OF (Semantic version) elsif (4 <= comparator) && (comparator <= 5) begin match = false user_value_version = Semantic::Version.new(user_value.to_s.strip()) ((comparison_value.to_s.split(",").map { |x| x.strip() }).reject { |c| c.empty? }).each { |x| version = Semantic::Version.new(x) match = (user_value_version == version) || match } if match && comparator == 4 || !match && comparator == 5 ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end rescue ArgumentError => e ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)) next end # LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Semantic version) elsif (6 <= comparator) && (comparator <= 9) begin user_value_version = Semantic::Version.new(user_value.to_s.strip()) comparison_value_version = Semantic::Version.new(comparison_value.to_s.strip()) if (comparator == 6 && user_value_version < comparison_value_version) || (comparator == 7 && user_value_version <= comparison_value_version) || (comparator == 8 && user_value_version > comparison_value_version) || (comparator == 9 && user_value_version >= comparison_value_version) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end rescue ArgumentError => e ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)) next end elsif (10 <= comparator) && (comparator <= 15) begin user_value_float = Float(user_value.to_s.gsub(",", ".")) comparison_value_float = Float(comparison_value.to_s.gsub(",", ".")) if (comparator == 10 && user_value_float == comparison_value_float) || (comparator == 11 && user_value_float != comparison_value_float) || (comparator == 12 && user_value_float < comparison_value_float) || (comparator == 13 && user_value_float <= comparison_value_float) || (comparator == 14 && user_value_float > comparison_value_float) || (comparator == 15 && user_value_float >= comparison_value_float) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end rescue Exception => e ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)) next end # IS ONE OF (Sensitive) elsif comparator == 16 if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end # IS NOT ONE OF (Sensitive) elsif comparator == 17 if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s) ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)) return value, variation_id end end ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value)) end if rollout_percentage_items.size > 0 user_key = user.get_identifier() hash_candidate = ("%s%s" % [key, user_key]).encode("utf-8") hash_val = Digest::SHA1.hexdigest(hash_candidate)[0...7].to_i(base=16) % 100 bucket = 0 for rollout_percentage_item in rollout_percentage_items || [] bucket += rollout_percentage_item.fetch(PERCENTAGE, 0) if hash_val < bucket percentage_value = rollout_percentage_item.fetch(VALUE, nil) variation_id = rollout_percentage_item.fetch(VARIATION_ID, default_variation_id) ConfigCat.logger.info("Evaluating %% options. Returning %s" % percentage_value) return percentage_value, variation_id end end end return_value = setting_descriptor.fetch(VALUE, default_value) return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id) ConfigCat.logger.info("Returning %s" % return_value) return return_value, return_variation_id end
Private Class Methods
format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value)
click to toggle source
# File lib/configcat/rolloutevaluator.rb, line 172 def self.format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value) return "Evaluating rule: [%s:%s] [%s] [%s] => match, returning: %s" % [comparison_attribute, user_value, COMPARATOR_TEXTS[comparator], comparison_value, value] end
format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value)
click to toggle source
# File lib/configcat/rolloutevaluator.rb, line 176 def self.format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value) return "Evaluating rule: [%s:%s] [%s] [%s] => no match" % [comparison_attribute, user_value, COMPARATOR_TEXTS[comparator], comparison_value] end
format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, error)
click to toggle source
# File lib/configcat/rolloutevaluator.rb, line 180 def self.format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, error) return "Evaluating rule: [%s:%s] [%s] [%s] => SKIP rule. Validation error: %s" % [comparison_attribute, user_value, COMPARATOR_TEXTS[comparator], comparison_value, error] end