class Determinator::Feature

A model for an individual feature or experiment

@attr_reader [nil,Hash<String,Integer>] variants The variants for this experiment, with the name of the variant as the key and the weight as the value. Will be nil for non-experiments.

Attributes

active[R]
bucket_type[R]
fixed_determinations[R]
identifier[R]
name[R]
overrides[R]
structured_bucket[R]
target_groups[R]
variants[R]
winning_variant[R]

Public Class Methods

new(name:, identifier:, bucket_type:, target_groups:, structured_bucket: nil, fixed_determinations: [], variants: {}, overrides: {}, active: false, winning_variant: nil) click to toggle source
# File lib/determinator/feature.rb, line 8
def initialize(name:, identifier:, bucket_type:, target_groups:, structured_bucket: nil, fixed_determinations: [], variants: {}, overrides: {}, active: false, winning_variant: nil)
  @name = name.to_s
  @identifier = identifier.to_s
  @variants = variants
  @target_groups = parse_target_groups(target_groups)
  @fixed_determinations = parse_fixed_determinations(fixed_determinations)
  @winning_variant = parse_outcome(winning_variant, allow_exclusion: false)
  @active = active
  @bucket_type = bucket_type.to_sym
  @structured_bucket = structured_bucket

  # To prevent confusion between actor id data types
  @overrides = overrides.each_with_object({}) do |(identifier, outcome), hash|
    parsed = parse_outcome(outcome, allow_exclusion: true)
    hash[identifier.to_s] = parsed unless parsed.nil?
  end
end

Public Instance Methods

==(other) click to toggle source
# File lib/determinator/feature.rb, line 63
def ==(other)
  Marshal.dump(self) == Marshal.dump(other)
end
active?() click to toggle source
# File lib/determinator/feature.rb, line 26
def active?
  !!active
end
experiment?() click to toggle source

@return [true,false] Is this feature an experiment?

# File lib/determinator/feature.rb, line 31
def experiment?
  variants.any?
end
feature_flag?() click to toggle source

@return [true,false] Is this feature a feature flag?

# File lib/determinator/feature.rb, line 36
def feature_flag?
  variants.empty?
end
overridden_for?(id) click to toggle source

Is this feature overridden for the given actor id?

@return [true,false] Whether this feature is overridden for this actor

# File lib/determinator/feature.rb, line 48
def overridden_for?(id)
  overrides.has_key?(id.to_s)
end
override_value_for(id) click to toggle source
# File lib/determinator/feature.rb, line 52
def override_value_for(id)
  overrides[id.to_s]
end
parse_outcome(outcome, allow_exclusion:) click to toggle source

Validates the given outcome for this feature.

# File lib/determinator/feature.rb, line 57
def parse_outcome(outcome, allow_exclusion:)
  valid_outcomes = experiment? ? variants.keys : [true]
  valid_outcomes << false if allow_exclusion
  valid_outcomes.include?(outcome) ? outcome : nil
end
structured?() click to toggle source

@return [true,false] Is this feature using structured identification?

# File lib/determinator/feature.rb, line 41
def structured?
  !!structured_bucket && !structured_bucket.empty?
end
to_explain_params() click to toggle source
# File lib/determinator/feature.rb, line 67
def to_explain_params
  { name: name, identifier: identifier, bucket_type: bucket_type }
end

Private Instance Methods

parse_fixed_determination(fixed_determination) click to toggle source
# File lib/determinator/feature.rb, line 99
def parse_fixed_determination(fixed_determination)
  return fixed_determination if fixed_determination.is_a? FixedDetermination

  variant = fixed_determination['variant']
  return nil if variant && !variants.keys.include?(variant)

  # if a variant is present the fixed determination should always be on
  return nil if variant && !fixed_determination['feature_on']

  constraints = fixed_determination['constraints'].to_h

  FixedDetermination.new(
    name: fixed_determination['name'],
    feature_on: fixed_determination['feature_on'],
    variant: variant,
    constraints: constraints
  )
# Invalid fixed determinations are ignored
rescue
  nil
end
parse_fixed_determinations(fixed_determinations) click to toggle source
# File lib/determinator/feature.rb, line 95
def parse_fixed_determinations(fixed_determinations)
  fixed_determinations.map(&method(:parse_fixed_determination)).compact
end
parse_target_group(target_group) click to toggle source
# File lib/determinator/feature.rb, line 79
def parse_target_group(target_group)
  return target_group if target_group.is_a? TargetGroup

  constraints = target_group['constraints'].to_h

  TargetGroup.new(
    name: target_group['name'],
    rollout: target_group['rollout'].to_i,
    constraints: constraints
  )

# Invalid target groups are ignored
rescue
  nil
end
parse_target_groups(target_groups) click to toggle source
# File lib/determinator/feature.rb, line 75
def parse_target_groups(target_groups)
  target_groups.map(&method(:parse_target_group)).compact
end