Class: Toggly::Evaluators::ContextProperty

Inherits:
Base
  • Object
show all
Defined in:
lib/toggly/evaluators/context_property.rb

Overview

Evaluator for ContextProperty entity filters. Fail closed.

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#handles?

Class Method Details

.compare(actual, operator, expected, value_type) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/toggly/evaluators/context_property.rb', line 43

def self.compare(actual, operator, expected, value_type)
  case operator
  when "eq"
    actual.to_s.casecmp?(expected)
  when "neq"
    !actual.to_s.casecmp?(expected)
  when "gt", "gte", "lt", "lte"
    compare_ordered(actual, expected, value_type, operator)
  when "in"
    expected.split(",").map(&:strip).reject(&:empty?).any? { |c| c.casecmp?(actual.to_s) }
  when "contains"
    if value_type == "string[]"
      Array(actual).any? { |v| v.to_s.casecmp?(expected) }
    else
      actual.to_s.downcase.include?(expected.downcase)
    end
  else
    false
  end
end

.compare_ordered(actual, expected, value_type, operator) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/toggly/evaluators/context_property.rb', line 64

def self.compare_ordered(actual, expected, value_type, operator)
  if value_type == "datetime"
    a = Time.parse(actual.to_s)
    e = Time.parse(expected.to_s)
    cmp = a <=> e
  elsif value_type == "number"
    a = parse_number(actual)
    e = parse_number(expected)
    return false if a.nil? || e.nil?

    cmp = a <=> e
  else
    return false
  end
  case operator
  when "gt" then cmp.positive?
  when "gte" then cmp >= 0
  when "lt" then cmp.negative?
  when "lte" then cmp <= 0
  else false
  end
rescue ArgumentError, TypeError
  false
end

.context_property?(rule) ⇒ Boolean

Returns:

  • (Boolean)


18
19
20
21
# File 'lib/toggly/evaluators/context_property.rb', line 18

def self.context_property?(rule)
  type = rule["type"] || rule[:type] || rule["name"] || rule[:name]
  type.to_s.downcase == "contextproperty"
end

.evaluate_single(rule, entity) ⇒ Object



23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/toggly/evaluators/context_property.rb', line 23

def self.evaluate_single(rule, entity)
  property = rule_lookup(rule, "Property")
  operator = rule_lookup(rule, "Operator")
  expected = rule_lookup(rule, "Value")
  value_type = (rule_lookup(rule, "ValueType") || "string").to_s.downcase
  return false if property.to_s.strip.empty? || operator.to_s.strip.empty? || expected.nil?

  operator = operator.to_s.downcase
  return false unless entity.attribute?(property)

  actual = entity.attribute(property)
  compare(actual, operator, expected.to_s, value_type)
end

.parse_number(value) ⇒ Object

Strict numeric parse. Invalid strings must not coerce to 0.0.



90
91
92
93
94
95
96
97
# File 'lib/toggly/evaluators/context_property.rb', line 90

def self.parse_number(value)
  n = Float(value)
  return nil unless n.finite?

  n
rescue ArgumentError, TypeError
  nil
end

.rule_lookup(rule, key) ⇒ Object



37
38
39
40
41
# File 'lib/toggly/evaluators/context_property.rb', line 37

def self.rule_lookup(rule, key)
  params = rule["parameters"] || rule[:parameters] || rule
  params[key] || params[key.to_s] || params[key.to_sym] ||
    params.find { |k, _| k.to_s.casecmp?(key.to_s) }&.last
end

.typeObject



7
8
9
# File 'lib/toggly/evaluators/context_property.rb', line 7

def self.type
  "contextproperty"
end

Instance Method Details

#evaluate(rule, context, feature_key: nil) ⇒ Object



11
12
13
14
15
16
# File 'lib/toggly/evaluators/context_property.rb', line 11

def evaluate(rule, context, feature_key: nil)
  entity = context&.entity
  return false unless entity

  self.class.evaluate_single(rule, entity)
end