Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
336 changes: 306 additions & 30 deletions api.yaml

Large diffs are not rendered by default.

101 changes: 28 additions & 73 deletions lib/ff/ruby/server/sdk/api/evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@ class Evaluator < Evaluation
def initialize(repository, logger = nil)

unless repository.kind_of?(Repository)

raise "The 'repository' parameter must be of '" + Repository.to_s + "' data type"
end

if logger != nil

@logger = logger
else

@logger = Logger.new(STDOUT)
end

Expand All @@ -30,7 +27,6 @@ def bool_variation(identifier, target, default_value, callback)
variation = evaluate(identifier, target, "boolean", callback)

if variation != nil

return variation.value == "true"
end

Expand All @@ -43,7 +39,6 @@ def string_variation(identifier, target, default_value, callback)
variation = evaluate(identifier, target, "string", callback)

if variation != nil

return variation.value
end

Expand All @@ -56,7 +51,6 @@ def integer_variation(identifier, target, default_value, callback)
variation = evaluate(identifier, target, "int", callback)

if variation != nil

return variation.value.to_i
end

Expand All @@ -68,7 +62,6 @@ def number_variation(identifier, target, default_value, callback)
variation = evaluate(identifier, target, "int", callback)

if variation != nil

return variation.value.to_f
end

Expand All @@ -81,7 +74,6 @@ def json_variation(identifier, target, default_value, callback)
variation = evaluate(identifier, target, "json", callback)

if variation != nil

return JSON.parse(variation.value)
end

Expand All @@ -92,36 +84,27 @@ def json_variation(identifier, target, default_value, callback)
def evaluate(identifier, target, expected, callback)

if callback != nil

unless callback.kind_of?(FlagEvaluateCallback)

raise "The 'callback' parameter must be of '" + FlagEvaluateCallback.to_s + "' data type"
end
end

flag = @repository.get_flag(identifier)

if flag != nil && flag.kind == expected

unless flag.prerequisites.empty?

pre_req = check_pre_requisite(flag, target)

unless pre_req

return find_variation(flag.variations, flag.off_variation)
end
end

variation = evaluate_flag(flag, target)

if variation != nil

if callback != nil

callback.process_evaluation(flag, target, variation)
end

return variation
end
end
Expand All @@ -134,24 +117,16 @@ def evaluate(identifier, target, expected, callback)
def get_attr_value(target, attribute)

if attribute != nil && !attribute.empty?

if target.respond_to?(attribute, :include_private)

@logger.debug "The attribute " + attribute.to_s + " exists (1)"

return target.send(attribute)
else

@logger.debug "target attrs: " + target.attributes.to_s

result = target.attributes[attribute.to_sym]

if result == nil

@logger.debug "The attribute " + attribute.to_s + " does not exist"

else

@logger.debug "The attribute " + attribute.to_s + " exists (2)"
end

Expand All @@ -167,11 +142,8 @@ def get_attr_value(target, attribute)
def find_variation(variations, identifier)

if identifier != nil && !identifier.empty?

variations.each do |v|

if v.identifier == identifier

return v
end
end
Expand Down Expand Up @@ -225,12 +197,22 @@ def evaluate_distribution(distribution, target)
nil
end


def evaluate_clauses_v2(clauses, target)
# New style rules require that all clauses are true
clauses.each do |clause|
if !evaluate_clause(clause, target)
return false
end
end
true
end

def evaluate_clauses(clauses, target)

clauses.each do |clause|

if evaluate_clause(clause, target)

return true
end
end
Expand All @@ -241,75 +223,62 @@ def evaluate_clauses(clauses, target)
def evaluate_clause(clause, target)

if clause == nil

return false
end

operator = clause.op

if operator == nil || operator.empty?

return false
end

if operator == "segmentMatch"

return is_target_included_or_excluded_in_segment(clause.values, target)
end

if clause.values.empty?

return false
end

value = clause.values[0]
attr_value = get_attr_value(target, clause.attribute)

if attr_value == nil

return false
end

object = attr_value.to_s

if operator == "starts_with"

return object.start_with?(value)
end

if operator == "ends_with"

return object.end_with?(value)
end

if operator == "match"

match = object.match?(value)
return match != nil && !match.empty?
end

if operator == "contains"

return object.include?(value)
end

if operator == "equal"

return object.casecmp?(value)
end

if operator == "equal_sensitive"

return object == value
end

if operator == "in"

return clause.values.include?(object)
end

if operator == "segmentMatch"

return is_target_included_or_excluded_in_segment(clause.values, target)
end

Expand All @@ -325,27 +294,35 @@ def is_target_included_or_excluded_in_segment(segment_list, target)
if segment != nil

if is_target_in_list(target, segment.excluded)

@logger.debug "Target " + target.name.to_s + " excluded from segment " + segment.name.to_s + " via exclude list"

return false
end

if is_target_in_list(target, segment.included)

@logger.debug "Target " + target.name.to_s + " included in segment " + segment.name.to_s + " via include list"

return true
end

rules = segment.rules

if rules != nil && !rules.empty? && evaluate_clauses(rules, target)
new_serving_rules = segment.serving_rules
if new_serving_rules != nil && !new_serving_rules.empty?
# Use enhanced rules first if they're available

@logger.debug "Target " + target.name.to_s + " included in segment " + segment.name.to_s + " via rules"
new_serving_rules.sort_by!(&:priority)
new_serving_rules.each do |serving_rule|
if evaluate_clauses_v2(serving_rule.clauses, target)
return true
end
end

return true
else
# Fall back to old rules
rules = segment.rules
if rules != nil && !rules.empty? && evaluate_clauses(rules, target)
@logger.debug "Target " + target.name.to_s + " included in segment " + segment.name.to_s + " via rules"
return true
end
end

end
end

Expand All @@ -355,26 +332,21 @@ def is_target_included_or_excluded_in_segment(segment_list, target)
def evaluate_rules(serving_rules, target)

if target == nil || serving_rules == nil

return nil
end

sorted = serving_rules.sort do |a, b|

b.priority <=> a.priority
end

sorted.each do |rule|

next unless evaluate_rule(rule, target)

if rule.serve.distribution != nil

return evaluate_distribution(rule.serve.distribution, target)
end

if rule.serve.variation != nil

return rule.serve.variation
end
end
Expand All @@ -383,14 +355,12 @@ def evaluate_rules(serving_rules, target)
end

def evaluate_rule(serving_rule, target)

evaluate_clauses(serving_rule.clauses, target)
end

def evaluate_variation_map(variation_maps, target)

if target == nil

return nil
end

Expand All @@ -399,28 +369,22 @@ def evaluate_variation_map(variation_maps, target)
targets = variation_map.targets

if targets != nil

found = nil

targets.each do |t|

if t.identifier != nil && t.identifier == target.identifier

found = t
break
end
end

if found != nil

return variation_map.variation
end
end

segment_identifiers = variation_map.target_segments

if segment_identifiers != nil && is_target_included_or_excluded_in_segment(segment_identifiers, target)

return variation_map.variation
end
end
Expand All @@ -433,32 +397,26 @@ def evaluate_flag(feature_config, target)
variation = feature_config.off_variation

if feature_config.state == OpenapiClient::FeatureState::ON

variation = nil

if feature_config.variation_to_target_map != nil

variation = evaluate_variation_map(feature_config.variation_to_target_map, target)
end

if variation == nil

variation = evaluate_rules(feature_config.rules, target)
end

if variation == nil

variation = evaluate_distribution(feature_config.default_serve.distribution, target)
end

if variation == nil

variation = feature_config.default_serve.variation
end
end

if variation != nil

return find_variation(feature_config.variations, variation)
end

Expand Down Expand Up @@ -519,11 +477,8 @@ def check_pre_requisite(parent_feature_config, target)
def is_target_in_list(target, list_of_targets)

if list_of_targets != nil

list_of_targets.each do |included_target|

if included_target.identifier.include?(target.identifier)

return true
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/ff/ruby/server/sdk/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Ruby
module Server
module Sdk

VERSION = "1.2.1"
VERSION = "1.3.0"
end
end
end
Expand Down
Loading