11require "date"
2+ require "semantic"
23
34module LaunchDarkly
45 module Evaluation
56 BUILTINS = [ :key , :ip , :country , :email , :firstName , :lastName , :avatar , :name , :anonymous ]
67
8+ NUMERIC_VERSION_COMPONENTS_REGEX = Regexp . new ( "^[0-9.]*" )
9+
10+ DATE_OPERAND = lambda do |v |
11+ if v . is_a? String
12+ begin
13+ DateTime . rfc3339 ( v ) . strftime ( "%Q" ) . to_i
14+ rescue => e
15+ nil
16+ end
17+ elsif v . is_a? Numeric
18+ v
19+ else
20+ nil
21+ end
22+ end
23+
24+ SEMVER_OPERAND = lambda do |v |
25+ if v . is_a? String
26+ for _ in 0 ..2 do
27+ begin
28+ return Semantic ::Version . new ( v )
29+ rescue ArgumentError
30+ v = addZeroVersionComponent ( v )
31+ end
32+ end
33+ end
34+ nil
35+ end
36+
37+ def self . addZeroVersionComponent ( v )
38+ NUMERIC_VERSION_COMPONENTS_REGEX . match ( v ) { |m |
39+ m [ 0 ] + ".0" + v [ m [ 0 ] . length ..-1 ]
40+ }
41+ end
42+
43+ def self . comparator ( converter )
44+ lambda do |a , b |
45+ av = converter . call ( a )
46+ bv = converter . call ( b )
47+ if !av . nil? && !bv . nil?
48+ yield av <=> bv
49+ else
50+ return false
51+ end
52+ end
53+ end
54+
755 OPERATORS = {
856 in :
957 lambda do |a , b |
@@ -42,33 +90,15 @@ module Evaluation
4290 ( a . is_a? Numeric ) && ( a >= b )
4391 end ,
4492 before :
45- lambda do |a , b |
46- begin
47- if a . is_a? String
48- a = DateTime . rfc3339 ( a ) . strftime ( '%Q' ) . to_i
49- end
50- if b . is_a? String
51- b = DateTime . rfc3339 ( b ) . strftime ( '%Q' ) . to_i
52- end
53- ( a . is_a? Numeric ) ? a < b : false
54- rescue => e
55- false
56- end
57- end ,
93+ comparator ( DATE_OPERAND ) { |n | n < 0 } ,
5894 after :
59- lambda do |a , b |
60- begin
61- if a . is_a? String
62- a = DateTime . rfc3339 ( a ) . strftime ( "%Q" ) . to_i
63- end
64- if b . is_a? String
65- b = DateTime . rfc3339 ( b ) . strftime ( "%Q" ) . to_i
66- end
67- ( a . is_a? Numeric ) ? a > b : false
68- rescue => e
69- false
70- end
71- end
95+ comparator ( DATE_OPERAND ) { |n | n > 0 } ,
96+ semVerEqual :
97+ comparator ( SEMVER_OPERAND ) { |n | n == 0 } ,
98+ semVerLessThan :
99+ comparator ( SEMVER_OPERAND ) { |n | n < 0 } ,
100+ semVerGreaterThan :
101+ comparator ( SEMVER_OPERAND ) { |n | n > 0 }
72102 }
73103
74104 class EvaluationError < StandardError
@@ -223,7 +253,10 @@ def variation_for_user(rule, user, flag)
223253 def bucket_user ( user , key , bucket_by , salt )
224254 return nil unless user [ :key ]
225255
226- id_hash = user_value ( user , bucket_by )
256+ id_hash = bucketable_string_value ( user_value ( user , bucket_by ) )
257+ if id_hash . nil?
258+ return 0.0
259+ end
227260
228261 if user [ :secondary ]
229262 id_hash += "." + user [ :secondary ]
@@ -235,6 +268,12 @@ def bucket_user(user, key, bucket_by, salt)
235268 hash_val . to_i ( 16 ) / Float ( 0xFFFFFFFFFFFFFFF )
236269 end
237270
271+ def bucketable_string_value ( value )
272+ return value if value . is_a? String
273+ return value . to_s if value . is_a? Integer
274+ nil
275+ end
276+
238277 def user_value ( user , attribute )
239278 attribute = attribute . to_sym
240279
0 commit comments